optimising the search time in hashmap - java

I have a csv file which is hashmapped, whenever the user enter the city name(key) it will display all the details of that city. I have to optimize the search result time, everytime the it is reading the file(instead of only once) and displaying the values.
The CSV files contains data like this :
city,city_ascii,lat,lng,country,iso2,iso3,admin_name,capital,population,id
Malishevë,Malisheve,42.4822,20.7458,Kosovo,XK,XKS,Malishevë,admin,,1901597212
Prizren,Prizren,42.2139,20.7397,Kosovo,XK,XKS,Prizren,admin,,1901360309
Zubin Potok,Zubin Potok,42.9144,20.6897,Kosovo,XK,XKS,Zubin
Potok,admin,,1901608808
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;
import java.io.IOException;
public class CSVFileReaders{
public static void main(String[] args) {
String filePath = "C:\\worldcities1.csv";
Scanner in = new Scanner(System.in);
System.out.println(" \n Enter the City name to be Searched : \n _> ");
long start = System.currentTimeMillis();
String searchTerm = in.nextLine();
readAndFindRecordFromCSV(filePath, searchTerm);
long end = System.currentTimeMillis();
System.out.println(" \n It took " + (end - start) + " Milli Seconds to search the result \n");
in.close();
}
public static void readAndFindRecordFromCSV( String filePath, String searchTerm) {
try{
HashMap<String,ArrayList<String>> cityMap = new HashMap<String,ArrayList<String>>();
Scanner x = new Scanner (new File(filePath),"UTF-8");
String city= "";
while(x.hasNextLine()) {
ArrayList<String> values = new ArrayList<String>();
String name = x.nextLine();
//break each line of the csv file to its elements
String[] line = name.split(",");
city = line[1];
for(int i=0;i<line.length;i++){
values.add(line[i]);
}
cityMap.put(city,values);
}
x.close();
//Search the city
if(cityMap.containsKey(searchTerm)) {
System.out.println("City name is : "+searchTerm+"\nCity details are accordingly in the order :"
+ "\n[city , city_ascii , lat , lng , country , iso2 , iso3 , admin_name , capital , population , id] \n"
+cityMap.get(searchTerm)+"");
}
else {
System.out.println("Enter the correct City name");
}
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}`
the time should be optimized and every time i search it is reading the entire file(which should happen)

Currently you mix the map initialization inside the search function.
You don't want that.
First, init the map, then use it in the search function.
To do that, extract a method for statements that instantiate and value the map and then refactor the readAndFindRecordFromCSV() method so that it accepts a Map as additional parameter :
public static void readAndFindRecordFromCSV( String filePath, String searchTerm, HashMap<String,ArrayList<String>> dataByCity) {...}
With refactoring IDE features, it should be simple enough : "extracting method" then "change signature".
Here is a code (not tested at runtime but tested at compile time) that splits the logical in separated tasks and also rely on instance methods :
public class CSVFileReaders {
private final String csvFile;
private HashMap<String, ArrayList<String>> cityMap;
private final Scanner in = new Scanner(System.in);
public static void main(String[] args) {
String filePath = "C:\\worldcities1.csv";
CSVFileReaders csvFileReaders = new CSVFileReaders(filePath);
csvFileReaders.createCitiesMap();
csvFileReaders.processUserFindRequest(); // First search
csvFileReaders.processUserFindRequest(); // Second search
}
public CSVFileReaders(String csvFile) {
this.csvFile = csvFile;
}
public void createCitiesMap() {
cityMap = new HashMap<>();
try (Scanner x = new Scanner(new File(csvFile), "UTF-8")) {
String city = "";
while (x.hasNextLine()) {
ArrayList<String> values = new ArrayList<String>();
String name = x.nextLine();
//break each line of the csv file to its elements
String[] line = name.split(",");
city = line[1];
for (int i = 0; i < line.length; i++) {
values.add(line[i]);
}
cityMap.put(city, values);
}
x.close();
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
public void processUserFindRequest() {
System.out.println(" \n Enter the City name to be Searched : \n _> ");
long start = System.currentTimeMillis();
String searchTerm = in.nextLine();
long end = System.currentTimeMillis();
System.out.println(" \n It took " + (end - start) + " Milli Seconds to search the result \n");
//Search the city
if (cityMap.containsKey(searchTerm)) {
System.out.println("City name is : " + searchTerm + "\nCity details are accordingly in the order :"
+ "\n[city , city_ascii , lat , lng , country , iso2 , iso3 , admin_name , capital , population , id] \n"
+ cityMap.get(searchTerm) + "");
} else {
System.out.println("Enter the correct City name");
}
}
}
The interesting part is here :
String filePath = "C:\\worldcities1.csv";
CSVFileReaders csvFileReaders = new CSVFileReaders(filePath);
csvFileReaders.createCitiesMap();
csvFileReaders.processUserFindRequest(); // First search
csvFileReaders.processUserFindRequest(); // Second search
The logical is clearer now.

Why do you create / load the CSV into a HashMap with every search ?
Just create the HashMap only once in the beginning, and then on every search just check whether it exists in the HashMap, eg move the read part into a separate method :
HashMap<String,ArrayList<String>> cityMap = new HashMap<String,ArrayList<String>>();
public static void readCSVIntoHashMap( String filePath) {
try{
Scanner x = new Scanner (new File(filePath),"UTF-8");
String city= "";
while(x.hasNextLine()) {
ArrayList<String> values = new ArrayList<String>();
String name = x.nextLine();
//break each line of the csv file to its elements
String[] line = name.split(",");
city = line[1];
for(int i=0;i<line.length;i++){
values.add(line[i]);
}
cityMap.put(city,values);
}
x.close();
...
}
Then have a separate method for searching :
public static void search(String searchTerm) {
if(cityMap.containsKey(searchTerm)) {
...
}
}

Related

Is there a way to collect data in multiple excel files?

there are 12 excel files that contain different products sales data, I try to sum up each month and entire year's sales using java but I can't add up sales for the whole year. Is there a way to do that? Thanks for your help.
public class Main {
public static void listOfSales(String fileName,String month){
List<PriceList> list = readsListFroExcel(fileName);
int sumOfInYearSale = 0;
int sumOfInOnlineSale =0;
int sumOfTotalMonthlySale = 0;
for (PriceList x : list){
sumOfInYearSale = sumOfInYearSale + x.getTotalPhysicalSale();
sumOfInOnlineSale = sumOfInYearSale + x.getTotalOnlineSale();
}
System.out.println(month +" Physical Sales : "+sumOfInYearSale);
System.out.println(month+ " Online Sales :" +sumOfInOnlineSale);
}
private static List<PriceList> readsListFroExcel(String fileName) {
List<PriceList> list = new ArrayList<>();
Path pathTofile = Paths.get(fileName);
try (BufferedReader br = Files.newBufferedReader(pathTofile)){
String headerLine = br.readLine();
String line = br.readLine();
while (line!=null){
String[] attributes = line.split(",");
PriceList listOfPrice = createList(attributes);
list.add(listOfPrice);
line = br.readLine();
}
} catch (IOException e) {
e.printStackTrace();
}
return list;
}
}

infinite loop stuck and having problem with code in java

the task at hand is to make a Blood Transfusion Manager application that efficiently chooses donors for blood transfusion.
When the application is launched it should try to open two files: “donors.txt” and “recipients.txt”. If one of the files is missing the program should announce the problem and exit. Both files should be formatted in the following way: each row should contain a person’s full name and their blood type separated by semicolon. The program should first read donors.txt, split each line into name and blood type and store the resulting array as a new element in a donors arraylist. It should also print the list on the screen and check that each person’s blood type is valid. If an invalid blood type is found that entry should not be added to the arraylist and the user should be notified which entry had a problem. Recipients should then be read, processed and stored (in recipients arraylist) in a similar manner this is part one of the task
this is my previous code and after updating it more to get closer to the end of my task i found that the code stopped work i debugged it and found that it is stuck in an infinite loop not sure how to fix it to or if there any other way to rewrite it to work maybe not using a while loop
package java_assigment_4;
import java.io.*;
import java.util.*;
public class java_assigment_4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner file_donors;
Scanner file_recp;
ArrayList list_donors = new ArrayList();
ArrayList list_recp = new ArrayList();
String blood_type = "o- o+ a- a+ b- b+ ab- ab+";
try
{
file_donors = new Scanner(new FileReader("donors.text"));
while (file_donors.hasNextLine())
{
list_donors.add(file_donors.nextLine()); // store donors names & blood type in array list
}//end while
file_donors.close();
}//end try
catch (FileNotFoundException e)
{
System.out.println("Error: " + e.getMessage());
}//end catch
try
{
file_recp = new Scanner(new FileReader("recipients.text"));
while (file_recp.hasNextLine())
{
list_recp.add(file_recp.nextLine()); // store recipents names & blood type in array list
}//end while
file_recp.close();
}//end try
catch (FileNotFoundException e)
{
System.out.println("Error: " + e.getMessage());
}//end catch
System.out.println("donors " + list_donors);
System.out.println("\n");
System.out.println("recipents " + list_recp);
// for (int i=0;i<list_donors.size();i++) {
// list_donors.contains(";"); // need a substring to store type after ;
}
}
}
this code below is the lastest code and is the one that is not working
public class java_assigment_4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner file_donors;
Scanner file_recp;
ArrayList<String> list_donors = new ArrayList<String>();
ArrayList<String> list_recp = new ArrayList<String>();
String [] blood_type = {"o-","o+","a-","a+","b-","b+","ab-","ab+"};
try
{
file_donors = new Scanner(new FileReader("donors.txt"));
while (file_donors.hasNextLine())
{
for (int i=0;i<list_donors.size();i++) {
String donor=list_donors.get(i);
String type =donor.substring((donor.indexOf(";") + 1)); // look for ; and stores info after witch is the blood type into a substring so i can use for checking if vaild
type=type.trim();
for (int z=0;z<blood_type.length;z++) {
if (type.equals(blood_type [z])) { compares the two lists
list_donors.add(file_donors.nextLine()); // store donors names & blood if vaild type in array list
}
else {
System.out.println( "this person with blood;" + type + " is not valid ");
}
}
}
}
file_donors.close();
}
catch (FileNotFoundException e)
{
System.out.println("Error: " + e.getMessage());
}
System.out.println("donors " + list_donors);
try
{
file_recp = new Scanner(new FileReader("recipients.txt"));
while (file_recp.hasNextLine())
{
for (int i=0;i<list_recp.size();i++) {
String recp=list_recp.get(i);
String type =recp.substring((recp.indexOf(";") + 1));
type=type.trim();
for (int z=0;z<blood_type.length;z++) {
if (type.equals(blood_type [z])) { // compares the two lists
list_recp.add(file_recp.nextLine()); // store recp names & blood type if vaild in array list
}
else {
System.out.println( "this person with blood ;" + type + " is not valid ");
}
}
}
}
file_recp.close();
}
catch (FileNotFoundException e)
{
System.out.println("Error: " + e.getMessage());
}
// System.out.println("donors " + list_donors);
// System.out.println("\n");
// System.out.println("recipents " + list_recp);
}
}
The list_donors.size() will always return 0,
cause the list_donors is empty when it begins
so the code never call file_donors.nextLine()
and file_donors.hasNextLine() will be always true
public class java_assigment_4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner file_donors;
Scanner file_recp;
ArrayList<String> list_donors = new ArrayList<String>();
ArrayList<String> list_recp = new ArrayList<String>();
String [] blood_type = {"o-","o+","a-","a+","b-","b+","ab-","ab+"};
try
{
file_donors = new Scanner(new FileReader("donors.txt"));
// file_donors.hasNextLine() always true in your code
while (file_donors.hasNextLine())
{
// list_donors.size() will return 0, cause the list_donors is empty when it begins
// so the code never enter this for and never call file_donors.nextLine()
for (int i=0;i<list_donors.size();i++) {
...
}
}
you can avoid this kind of situation doing something like
while (file_donors.hasNextLine())
{
string current_line = file_donors.hasNextLine();
updating with some code to help
import java.io.*;
import java.util.*;
public class Blood
{
public static void main(String[] args)
{
ArrayList<String> list_donors = new ArrayList<String>();
ArrayList<String> list_recp = new ArrayList<String>();
System.out.println("Starting!");
copyFromFile("/home/mitz/stackoverflow/java/Donors.txt", list_donors);
System.out.println("imported "+ list_donors.size() + " registers");
copyFromFile("/home/mitz/stackoverflow/java/Receptors.txt", list_recp);
System.out.println("imported "+ list_recp.size() + " registers");
System.out.println("Finished!");
}
public static void copyFromFile(String filename, ArrayList<String> listDestiny)
{
Scanner fileScanner;
FileReader fileReader;
try
{
fileReader = new FileReader(filename);
fileScanner = new Scanner(fileReader);
while (fileScanner.hasNextLine())
{
String currentLine = fileScanner.nextLine();
String type = currentLine.substring((currentLine.indexOf(";") + 1));
if(isValidBloodType(type))
{
listDestiny.add(currentLine);
System.out.println("Imported: " + currentLine);
}else{
System.out.println("Invalid blood type!! Alien detected with blood type: " + type);
}
}
fileScanner.close();
}
catch (FileNotFoundException e)
{
System.out.println("Arquivo não encontrado");
}
}
public static Boolean isValidBloodType(String type)
{
String[] blood_type = {"o-", "o+", "a-", "a+", "b-", "b+", "ab-", "ab+"};
return Arrays.asList(blood_type).contains(type);
}
}

Strings manipulation in java

I have a multiline String as below,I want to lift 'VC-38NN' whenever String line contains 'Profoma invoice'. My code below still prints everything once the search string is found.
Payment date
receipt serial
Profoma invoice VC-38NN
Welcome again
if(multilineString.toLowerCase().contains("Profoma invoice".toLowerCase()))
{
System.out.println(multilineString+"");
}
else
{
System.out.println("Profoma invoice not found");
}
Here are two possible solutions:
String input = "Payment date\n" +
"receipt serial\n" +
"Profoma invoice VC-38NN\n" +
"Welcome again";
// non-regex solution
String uppercased = input.toUpperCase();
// find "profoma invoice"
int profomaInvoiceIndex = uppercased.indexOf("PROFOMA INVOICE ");
if (profomaInvoiceIndex != -1) {
// find the first new line character after "profoma invoice".
int newLineIndex = uppercased.indexOf("\n", profomaInvoiceIndex);
if (newLineIndex == -1) { // if there is no new line after that, use the end of the string
newLineIndex = uppercased.length();
}
int profomaInvoiceLength = "profoma invoice ".length();
// substring from just after "profoma invoice" to the new line
String result = uppercased.substring(profomaInvoiceIndex + profomaInvoiceLength, newLineIndex);
System.out.println(result);
}
// regex solution
Matcher m = Pattern.compile("^profoma invoice (.+)$", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE).matcher(input);
if (m.find()) {
System.out.println(m.group(1));
}
Explanation in comments:
public class StackOverflow55313851 {
public final static String TEXT = "Profoma invoice";
public static void main(String[] args) {
String multilineString = "Payment date\n" +
"receipt serial\n" +
"Profoma invoice VC-38NN\n" +
"Welcome again";
// split text by line breaks
String[] lines = multilineString.split("\n");
// iterate over every line
for (String line : lines) {
// if it contains desired text
if (line.toLowerCase().contains(TEXT.toLowerCase())) {
// find position of desired text in this line
int indexOfInvoiceText = line.toLowerCase().indexOf(TEXT.toLowerCase());
// get only part of the line following the desired text
String invoiceNumber = line.substring(indexOfInvoiceText + TEXT.length() + 1);
System.out.println(invoiceNumber);
}
}
}
}

Getting a list from a text file in java

I currently have a database access class to pull the data from my list listed like so:
Pique:CBPique.png:41:22:55:91
Ronaldo:STRonaldo.png:89:85:92:91
...
The class is as follows:
class DatabaseAccess {
static DatabaseAccess dataAccessor;
static DatabaseAccess getInstance(String dbPath) {
if (dataAccessor == null) {
dataAccessor = new DatabaseAccess(dbPath);
}
return dataAccessor;
}
private DatabaseAccess(String dbPath) {
dbLocation = dbPath;
}
List<FootballPlayer> getCards() {
return this.getData();
}
private List<FootballPlayer> getData() {
List<FootballPlayer> theData = new ArrayList<FootballPlayer>();
// create a Scanner and grab the data . . .
Scanner scanner = null;
String dataPath = dbLocation + File.separator + "text" + File.separator + "players.db";
String imagePath = dbLocation + File.separator + "images";
try {
scanner = new Scanner(new File(dataPath));
} catch (FileNotFoundException fnf) {
System.out.println(fnf.getMessage());
System.exit(0);
}
// scan players.db file line-by-line
scanner.useDelimiter("\n");
while (scanner.hasNext()) {
String line = scanner.next().trim();
// trim used to trim for new line
String[] bits = line.split(":");
String t = bits[0]; // title
String imgFileName = bits[1]; // image file name
int pa = Integer.parseInt(bits[2]); // pace
int sh = Integer.parseInt(bits[3]); // shooting
int dr = Integer.parseInt(bits[4]); // dribbling
int ph = Integer.parseInt(bits[5]); // physical
// create the image
ImageIcon img = new ImageIcon(imagePath + File.separator + imgFileName);
// Create the business object
FootballPlayer player = new FootballPlayer(t, img, pa, sh, dr, ph);
// add it to the list ... simple as ...
theData.add(player);
}
scanner.close();
return theData;
}
What I want to do is pull the data from this list and display it / use it within another class, for example pulling the image file names for use, or even displaying all the data in a list.
Been struggling with it and any help would be really appreciated.
This could be a way your Scanner may look like: It reads the file and saves line per line into an ArrayList.
ArrayList<String> singleParts = new ArrayList<String>();
try {
int index = 0;
Scanner scanner = new Scanner( new File("files/"+filename+".txt") );
while ( scanner.hasNextLine() ) {
String actualLine = scanner.nextLine();
singleParts.add(actualLine);
}
scanner.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch(Throwable te) {
te.printStackTrace();
}
After this, you could iterate through your ArrayList line per line, char per char. To make things easier, you could use your ':' as a seperator.
Example first line, Name:
String name = "";
boolean saved = false;
for(int line = 0; line < singleParts.size(); line++) {
for(int char = 0; char < singleParts.get(line).length(); char++) {
if(char<singleParts.get(line).indexOf(':') {
name += singleParts.get(line).charAt(char);
}
}
}
To get the rest, you have a lot of possibilities. For example, cut away the already used chars and reuse the loop similar to this one.
Does your Player object have attributes/properties that are unique, such as name or ID?
Since you already have the Player object containing all the properties you need, why not put it in a HashMap instead of Arraylist, considering if you have a key to identify the specific Player?
FootballPlayer player = new FootballPlayer(t, img, pa, sh, dr, ph);
yourKey = t // an identifier to you player, in this case i used t for example
playersMap.put(t,player);
This way you can easily retrieve your Player by using
Player myPlayer = playersMap.get(yourKey)
// you can then get the properties of the Player
myPlayer.getImg();
myPlayer.getPa();
Hope this helps

How can I count the number of cities per country from the data file?

How can I count the number of cities per country from the data file? I would also like to display the value as percentage of the total.
import java.util.StringTokenizer;
import java.io.*;
public class city
{
public static void main(String[] args)
{
String[] city = new String[120];
String country = null;
String[] latDegree =new String[120];
String lonDegree =null;
String latMinute =null;
String lonMinute =null;
String latDir = null;
String lonDir = null;
String time = null;
String amORpm = null;
try
{
File myFile = new File("CityLongandLat.txt");
FileReader fr = new FileReader(myFile);
BufferedReader br = new BufferedReader(fr);
String line = null;
int position =0;
int latitude=0;
while( (line = br.readLine()) != null)
{
// System.out.println(line);
StringTokenizer st = new StringTokenizer(line,",");
while(st.hasMoreTokens())
{
city[position] = st.nextToken();
country = st.nextToken();
latDegree[latitude] =st.nextToken();
latMinute =st.nextToken();
latDir = st.nextToken();
lonDegree =st.nextToken();
lonMinute =st.nextToken();
lonDir = st.nextToken();
time = st.nextToken();
amORpm = st.nextToken();
}
if(city.length<8)
{
System.out.print(city[position] + "\t\t");
}
else
{
System.out.print(city[position] + "\t");
}
if(country.length()<16)
{
System.out.print(country +"\t\t");
}
else
{
System.out.print(country);
}
System.out.print(latDegree + "\t");
System.out.print(latMinute + "\t");
System.out.print(latDir + "\t");
System.out.print(lonDegree + "\t");
System.out.print(lonMinute + "\t");
System.out.print(lonDir + "\t");
System.out.print(time + "\t");
System.out.println(amORpm + "\t");
position++;
}
br.close();
}
catch(Exception ex)
{
System.out.println("Error !!!");
}
}
}
One easy way that comes to my mind would be as follows...
Create a hashMap Object where the key is a string (the country) and the value is an integer (number of cities found for the country) so it would be something like
Map countryResultsFoundMap = new HashMap< String,Integer>();
In short, for each row you would pick the country, (I would recommend that you .trim() and .toLowerCase() the value first) and check if it is existing in the hashMap, if not, add the entry like countryResultsFoundMap.put(country,0), otherwise, if the country already exists the pick the value from the hashMAp and add +1 to its integer value.
Eventually you will have all the values stored in the map and you can have access to that data for your calculations.
Hope that helps
"here are some of the output from the data file from my programme"
Aberdeen Scotland 57 2 [Ljava.lang.String;#33906773 9 N [Ljava.lang.String;#4d7‌​7c977 9 W 05:00 p.m. Adelaide Australia 34 138 [Ljava.lang.String;#33906773 55 S [Ljava.lang.String;‌​#4d77c977 36 E 02:30 a.m...
The reason why your getting that output, is because you're trying to print the array object latDegree.
String[] latDegree
...
System.out.print(latDegree + "\t");
Also, you have lattitude = 0; but you never increment it, so it will always use the index 0 for the array. You need to increment it, like you did position++.
So for the print statement, print the print the value at index lattitude, not the entire array
Try this
System.out.print(latDegree[lattitude] + "\t");
...
lattitude++;
If for some reason you do want to print the array, then use Arrays.toString(array); or just iterate through it
I would also start with a map, and group the cities by country with a map.
Map<String,<List<String>>
Where the key is the country and the value is the list of cities in this country. With the size() methods you can perform the operations cities per country and percentage of total.
When you read one line you check if the key (country) already exists, if not you create a new list and add the city, otherwise add the city only to the existing list.
As a starter you could use the following snippet. However this sample assumes that the content of the file is read already and given as an argument to the method.
Map<String,List<String>> groupByCountry(List<String> lines){
Map<String,List<String>> group = new HashMap<>();
for (String line : lines) {
String[] tokens = line.split(",");
String city = tokens[0];
String country = tokens[1];
...
if(group.containsKey(country)){
group.get(country).add(city);
}else{
List<String> cities = new ArrayList<>();
cities.add(city);
group.put(country, cities);
}
}
return group;
}

Categories

Resources