Why is just reading a small file taking so long? - java

I'm doing what seems like a simple read from a data file, and it's taking for fricking ever. (And by that, I mean 300ms.) This is the code in question:
BufferedReader f = new BufferedReader(new FileReader("input.txt"));
StringTokenizer st = new StringTokenizer(f.readLine());
int var1 = Integer.parseInt(st.nextToken())
int var2 = Integer.parseInt(st.nextToken());
Integer[][] people = new Integer[var1][];
for(int i = 0; i < var2; i++)
f.readLine();
for(Integer i = 0; i < var1; i++)
{
StringTokenizer line = new StringTokenizer(f.readLine(), " \t\n\r\f,");
line.nextToken();
Integer[] list = new Integer[line.countTokens()];
for(int j = 0; j < drinks.length; j++)
list[j] = Integer.parseInt(line.nextToken());
people[i] = list;
}
And this is the relevant time output:
sammysmbp:fridgemadness_lean sfishman$ time java fridgemadness
real 0m0.311s
user 0m0.277s
sys 0m0.056s
Is it just me or is that really, really slow. It's only going through 51 lines of input in this example.

Just guessing, but probably a lot of the time is used up just for the jvm to start up.
Print a timestamp just before and after the relevant code. If my guess is correct a little server taking requests might help to eliminate the startup time.

You don't need \r or \n in the pattern, readLine() has already removed them.
JVM startup aside, most of the time will be spent in reading to the desired position if the file is at all long. Maybe a text file of lines isn't the correct data structure.

Related

Expand the range of numbers

I was trying a solve a issue which is bothering me for a while. I created a small parser that reads an .ini file and then stores the data in an ArrayList. However, I got stuck with the following snippet:
while (!(sCurrentLine.equals("[End]"))) {
formats.add(sCurrentLine);
for (int i = 0; formats.size() > 0; i++) {
}
sCurrentLine = br.readLine();
}
Now this is the place where I have to add values into formats, which is of type ArrayList.
The values that will be added like this:
0900.013-017=LABEL
0900.018-029=LABEL
Now the range is in between and I also have to make sure that '0900' and '=label' repeats themselves along with the expansion of numbers, for example:
0900.013=LABEL
0900.014=LABEL
0900.015=LABEL
0900.016=LABEL and so on...
and store it back in the ArrayList.
I don't want to depend upon third-party libraries. Please help me out with this.
Use a regular expression to parse the range, then loop over the parsed values. There is some fine tuning to be done but I think this should get you started.
Pattern rangePattern = Pattern.compile("([0-9]+)\\.([0-9]+)-([0-9]+)=(.*)$");
Matcher rangeMatcher = rangePattern.matcher("0900.13-17=First label");
if (rangeMatcher.matches()) {
String prefix = rangeMatcher.group(1);
int start = Integer.parseInt(rangeMatcher.group(2));
int end = Integer.parseInt(rangeMatcher.group(3));
String label = rangeMatcher.group(4);
for (int r = start; r < end; r++) {
System.out.println(prefix + "." + r + "=" + label);
}
}
Create the pattern once and then just get new matchers each time through your loop.
The results:
0900.13=First label
0900.14=First label
0900.15=First label
0900.16=First label

Java arrays formatting

I am nearly done with my project (technically it's finished), but I am having a big problem with little detail such as output formatting. I am fairly new to JAVA and would apreciatte any help you can provide.
I need to output 2 arrays (String and Int) in some sort of table format.
Example:
England 4.6
USA 2.6
Japan 7.8
etc
I need exact spacing between the characters. I'll give you one part of my code: (I can apply the logic to the rest of the program)
double beerStrenghts [] = new double [10];
for(int x = 0; x < beerStrenghts.length; x++){
beerStrenghts[x] = beerStrenghts()[x];
}
String beerName [] = new String [10];
for(int x = 0; x < beerName.length; x++){
beerName[x] = (beerName()[x]);
}
String lookup;
Scanner keyboard = new Scanner(System.in);
System.out.println("Please enter search criteria. Min 3 characters.");
lookup = keyboard.nextLine();
while(lookup.length() < 3){
System.out.println("Please enter at least 3 characters");
lookup = keyboard.nextLine();
}
Formatter fmt = new Formatter();
int indexInt;
boolean found;
for(int x = 0; x< beerName.length; x++){
found = beerName[x].toUpperCase().contains(lookup.toUpperCase());
if(found){
fmt.format("%12s%3d",beerName[x], beerStrenghts[x]);
System.out.println(fmt);
}
}
}
public static String [] beerName(){
String[] beerName = new String[10];
beerName[0] = "Heineken";
beerName[1] = "Bud Light";
beerName[2] = "Coors Light";
beerName[3] = "Leffe Blonde";
beerName[4] = "Budweiser";
beerName[5] = "Erdinger Non-Alcoholic";
beerName[6] = "Bud Premier Select";
beerName[7] = "Corona";
beerName[8] = "Barefoot Bohemian";
beerName[9] = "3 Monts";
return beerName;
}
public static double [] beerStrenghts(){
double beerStrenghts [] = new double [10];
beerStrenghts[0] = 4.0;
beerStrenghts[1] = 4.2;
beerStrenghts[2] = 4.3;
beerStrenghts[3] = 6.6;
beerStrenghts[4] = 5.0;
beerStrenghts[5] = 0.0;
beerStrenghts[6] = 7.4;
beerStrenghts[7] = 4.6;
beerStrenghts[8] = 4.0;
beerStrenghts[9] = 8.5;
return beerStrenghts;
You need to (re)read the javadoc for java.util.Formatter. In particular, it says that the formatting code d is for decimal integers. You are dealing with doubles, so the f code is probably more your style. (I'm just guessing though since you've been very light on details.)
Try something like
fmt.format("%-12s%3.1f",beerName[x], beerStrenghts[x]);
just change %3d to %.03for %.3f
like :
fmt.format("%-12s %.03f", beerName[x], beerStrenghts[x]);
%d is used for formatting integers.
also have a look at StringBuilder if you want different locales i.e different ways of formatting for different regions.
More about formatters with string builders here:https://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html
Big thanks for all help guys. And apologies for not providing enough details... I am also new in stack overflow.
This is my college project indeed (small part of it). Unfortunately majority of solutions-problems haven't being covered during classes - but I am doing my own researches(going very very well - almost done, formatting is the last part) and though I'll give my shoot here :)
Thanks for java.until.formatter reference dcsohl - I'll definitely go through it.
The solution given is almost working but I cant figure out what is the problem this time. Current output while searching for "bud":
Bud Light 4.2
Bud Light 4.2Budweiser 5.0
Bud Light 4.2Budweiser 5.0Bud Premier Select 7.4

Searching and sorting through an Arraylist

I am a new Java programmer and I am working on a project that requires me to read a text file that has Movie Reviews.
Once I've read the file, I am asked to search and sort through the Array of movies and return the total number of reviews for each movie as well as the average rate for each movie.
The portion I am currently stuck on is iterating through the Array list.
I am using an inner and outer for loop and I seem to be getting an infinite loop.
I would appreciate a second set of eyes. I have been staring at this project for a few days now and starting to not see mistakes.
Here is the code:
import java.io.*;
import java.util.*;
import java.lang.*;
public class MovieReviewApp {
public static void main(String[] args)
{
String strline = "";
String[] result = null;
final String delimit = "\\s+\\|\\s+";
String title ="";
//int rating = (Integer.valueOf(- 1));
ArrayList<MovieReview> movies = new ArrayList<MovieReview>();
//ArrayList<String> titles = new ArrayList<String>();
//ArrayList<Integer> ratings = new ArrayList<Integer>();
//HashMap<String, Integer> hm = new HashMap<String, Integer>();
//ListMultimap<String, Integer> hm = ArrayListMultimap.create();
try
{
BufferedReader f = new BufferedReader(new FileReader("/Users/deborahjaffe/Desktop/Java/midterm/movieReviewData.txt"));
while(true)
{
strline = f.readLine(); // reads line by line of text file
if(strline == null)
{
break;
}
result = strline.split(delimit, 2); //creates two strings
//hm.put(result[0], new Integer [] {Integer.valueOf(result[1])});
//hm.put(result[0], Integer.valueOf(result[1]));
// titles.add(result[0]);
//ratings.add(Integer.valueOf(result[1]));
MovieReview m = new MovieReview(result[0]);
movies.add(m);
MovieReview m2 = new MovieReview();
int rating = Integer.valueOf(result[1]);
int sz = movies.size();
for (int i = 0; i < sz; i++)
{
for (int j = 0; j < sz; j++)
{
m2 = movies.get(i);
if (movies.contains(m2))
{
m2.addRating(rating);
}
else
{
movies.add(m2);
m2.addRating(rating);
}
}
}
movies.toString();
//Collections.sort(movies);
} //end while
f.close();
//Set<String> keys = hm.keySet();
//Collection<Integer> values = hm.values();
} //end of try
catch(FileNotFoundException e)
{
System.out.println("Error: File not found");
}
catch(IOException e)
{
System.out.println("Error opening a file.");
}
} // end main
} // end class
Read the file first and then iterate through the list or map for searching, sorting etc. In the above code, close the while loop before iterating through the list.
If you want to iterate through the ArrayList you can use an enhanced for-loop to iterate through it. Note: while in an enhanced for-loop you cannot make changes to the ArrayList as the enhanced for-loop makes the ArrayList essentially (and temporarily) read-only. This would work for iterating through to pull values, but not for adding values. Because you are changing the ArrayList this won't work, but I just thought that you should know about it, if you don't already. The enhanced for-loop works like this, I will put separate parts in squiggle brackets,
for({Object Type of ArrayList} {Dummy Value} : {name of ArrayList}), so it would look like this: for(MovieReview x: movies).
Regarding the inner part of this nested for-loop:
for (int i = 0; i < sz; i++)
{
for (int j = 0; j < sz; j++)
{
m2 = movies.get(i);
if (movies.contains(m2))
{
m2.addRating(rating);
}
else
{
movies.add(m2);
m2.addRating(rating);
}
}
}
Why do you have the inner part? The j variable is never used for anything so the for-loop seems kind of useless. Unless of course you made a mistake at the top of the inner loop and meant to have m2 = movies.get(j); but that seems unlikely.
In regard to the infinite loop, the way you wrote the for-loops you should not be getting an infinite loop because they all increment to a certain value that is reachable. Your while-loop seems to run infinitely, but I do notice that you have a break if strline points to null. I assume this is guaranteed to occur at the end of the file, but I would suggest that you make the condition for your while-loop be while(scannerName.hasNext()). This will allow your while-loop to eventually terminate without having the extra code plus having a Scanner instead of a BufferedReader will be slightly more efficient and still do everything the BufferedReader can do and much more, like that method hasNext().
I hope this has helped. If you have other questions, let me know. Good luck.

Null Pointer that makes no sense to me?

Im currently working on a program and any time i call Products[1] there is no null pointer error however, when i call Products[0] or Products[2] i get a null pointer error. However i am still getting 2 different outputs almost like there is a [0] and 1 or 1 and 2 in the array. Here is my code
FileReader file = new FileReader(location);
BufferedReader reader = new BufferedReader(file);
int numberOfLines = readLines();
String [] data = new String[numberOfLines];
Products = new Product[numberOfLines];
calc = new Calculator();
int prod_count = 0;
for(int i = 0; i < numberOfLines; i++)
{
data = reader.readLine().split("(?<=\\d)\\s+|\\s+at\\s+");
if(data[i].contains("input"))
{
continue;
}
Products[prod_count] = new Product();
Products[prod_count].setName(data[1]);
System.out.println(Products[prod_count].getName());
BigDecimal price = new BigDecimal(data[2]);
Products[prod_count].setPrice(price);
for(String dataSt : data)
{
if(dataSt.toLowerCase().contains("imported"))
{
Products[prod_count].setImported(true);
}
else{
Products[prod_count].setImported(false);
}
}
calc.calculateTax(Products[prod_count]);
calc.calculateItemTotal(Products[prod_count]);
prod_count++;
This is the output :
imported box of chocolates
1.50
11.50
imported bottle of perfume
7.12
54.62
This print works System.out.println(Products[1].getProductTotal());
This becomes a null pointer System.out.println(Products[2].getProductTotal());
This also becomes a null pointer System.out.println(Products[0].getProductTotal());
You're skipping lines containing "input".
if(data[i].contains("input")) {
continue; // Products[i] will be null
}
Probably it would be better to make products an ArrayList, and add only the meaningful rows to it.
products should also start with lowercase to follow Java conventions. Types start with uppercase, parameters & variables start with lowercase. Not all Java coding conventions are perfect -- but this one's very useful.
The code is otherwise structured fine, but arrays are not a very flexible type to build from program logic (since the length has to be pre-determined, skipping requires you to keep track of the index, and it can't track the size as you build it).
Generally you should build List (ArrayList). Map (HashMap, LinkedHashMap, TreeMap) and Set (HashSet) can be useful too.
Second bug: as Bohemian says: in data[] you've confused the concepts of a list of all lines, and data[] being the tokens parsed/ split from a single line.
"data" is generally a meaningless term. Use meaningful terms/names & your programs are far less likely to have bugs in them.
You should probably just use tokens for the line tokens, not declare it outside/ before it is needed, and not try to index it by line -- because, quite simply, there should be absolutely no need to.
for(int i = 0; i < numberOfLines; i++) {
// we shouldn't need data[] for all lines, and we weren't using it as such.
String line = reader.readLine();
String[] tokens = line.split("(?<=\\d)\\s+|\\s+at\\s+");
//
if (tokens[0].equals("input")) { // unclear which you actually mean.
/* if (line.contains("input")) { */
continue;
}
When you offer sample input for a question, edit it into the body of the question so it's readable. Putting it in the comments, where it can't be read properly, is just wasting the time of people who are trying to help you.
Bug alert: You are overwriting data:
String [] data = new String[numberOfLines];
then in the loop:
data = reader.readLine().split("(?<=\\d)\\s+|\\s+at\\s+");
So who knows how large it is - depends on the success of the split - but your code relies on it being numberOfLines long.
You need to use different indexes for the line number and the new product objects. If you have 20 lines but 5 of them are "input" then you only have 15 new product objects.
For example:
int prod_count = 0;
for (int i = 0; i < numberOfLines; i++)
{
data = reader.readLine().split("(?<=\\d)\\s+|\\s+at\\s+");
if (data[i].contains("input"))
{
continue;
}
Products[prod_count] = new Product();
Products[prod_count].setName(data[1]);
// etc.
prod_count++; // last thing to do
}

Java data type problem

I am working with matrix in java. ( another story :) )
I want to read a CSV file and store it in a variable. I will manipulate values then again store it in CSV file. I used STRING as data type. But if CSV file has like 500 columns. It kill my program speed :(. I think this is not good data type. Which data type I can use to temporary store LONG TEXT?
If my question is not clear please ask questions. I will explain.
Thanks
P.S: I am reading one line and storing it in variable like this
String str;
str += read line by line from CSV;
here is the loop
String reduceM="";
for(int kk=0;kk<W2.getRowDimension();kk++){
for(int jj=0;jj<W2.getColumnDimension();jj++){
reduceM += Double.toString(reduceMatrix[kk][jj]);
}
System.out.println("\r\n");
}
Use a StringBuilder (or StringBuffer if you're using Java 1.5 or older):
StringBuilder builder = new StringBuilder();
for (int kk = 0; kk<W2.getRowDimension(); kk++) {
for(int jj = 0; jj < W2.getColumnDimension(); jj++) {
builder.append(reduceMatrix[kk][jj]);
}
}
This will avoid it creating a new (and increasingly long) string for each iteration of the two loops.
However, there are no commas or line-breaks in this code - I suspect you actually want something like this:
StringBuilder builder = new StringBuilder();
for (int kk = 0; kk < W2.getRowDimension(); kk++) {
for (int jj = 0; jj < W2.getColumnDimension(); jj++) {
builder.append(reduceMatrix[kk][jj])
.append(",");
}
builder.append("\n"); // Or whatever line terminator you want
}
Note that that will leave an extra comma at the end of each row - let me know if you want ideas of how to remove that.
See this article for why this could make a huge improvement to your running time. (Note that it's an old article, talking about StringBuffer rather than StringBuilder - the latter is just an unsynchronized version of the former.)
Use the 'StringBuffer' class for concatenations. It is much more efficient.
Take a look at this article for an explanation: here
EDIT - Sorry I did not see this was already answered
Prefer StringBuilder, there is a big difference in performance compared to the string concatenation(+).
In addition to the skeet's great answer; dont write to system.out if its not necessary, or if you want to write to console use buffers or write when loop finishes. It makes your program cumbersome because each time it is encountered in the loop System.out stream opens writes flushes and closes.

Categories

Resources