Reputation: 580
ArrayList<String[]> writtenClasses = new ArrayList<String[]>();
// usually there is functional code here that populates
// ArrayList<String[]> writtenClasses with variably 3000
// String[] objects always of exactly 8 lines each
ArrayList<String> processedClasses = new ArrayList<String>();
for(String[] classLines: writtenClasses)
{
for(String classLine: classLines)
{
processedClasses.add(classLine);
}
}
String result = "";
for(String fileLine: processedClasses)
{
result += fileLine + "\n";
}
My code is above. It works fine and produces exactly the result I want, just slowly. It takes about 10ms per item of ArrayList writtenClasses which is okay until I give it bigger jobs. I suspect that there is something there to do with ArrayLists that is taking so long, but timing and printing to console job stats after each run revealed little.
This above code is an adaptation of earlier code in hopes to improve efficiency. It does so by about 4%. The below code is the old method I used which takes just a little longer than the above.
for(String[] classLines: writtenClasses)
{
for(String classLine: classLines)
{
result += classLine + "\n";
}
writtenClasses.set(writtenClasses.indexOf(classLines), null);
}
I do writtenClasses.set(writtenClasses.indexOf(classLines), null);
merely for the purposes of memory efficiency, and my stats show that it uses memory more efficiently with an undetectable amount of CPU effort.
This is my second question here on StackOverflow and i've done my best to follow the rules, but if i'm asking this badly or being inadvertently inconsiderate in some way, please, highlight that to me and i'll address that. :)
Upvotes: 5
Views: 2442
Reputation: 328619
The problem has been pointed out by other answers. With Java 8, an alternative to the two nested loops and a StringBuilder is to use a stream and a joining collector*:
String result = writtenClasses.stream()
.flatMap(array -> Arrays.stream(array))
.collect(joining("\n"));
*requires import static java.util.Collectors.joining;
Upvotes: 3
Reputation: 417767
There is absolutely no use creating the intermediate processedClasses
list. Also, StringBuilder
will speed up significantly the process:
// Consider a large initial size to even avoid reallocation, here I used 64 KB
StringBuilder sb = new StringBuilder(65536);
for (String[] classLines : writtenClasses)
for (String lines : classLines)
sb.append(lines).append('\n');
// Note: you might not even need to convert it to String, read reasoning below
String result = sb.toString();
We build the content in a StringBuilder which implements the CharSequence
interface. Many classes accept CharSequence
s and not just String
s. A good example is a FileWriter
. In these cases you don't even need to convert the StringBuilder
to a String
because the StringBuilder
can be passed just as easily as its String
result which may be another performance advantage if the content is really big.
Upvotes: 3
Reputation: 1588
based on this question
ewall:
At what point do you switch to StringBuilder? When it effects memory or performance. Or when it might. If you're really only doing this for a couple strings once, no worries. But if you're going to be doing it over and over again, you should see a measurable difference when using StringBuilder.
StringBuilder myString = new StringBuilder();
for(String classLine: classLines)
{
myString.append(classLine).append("\n");
}
StringBuilder
would somehow improve your performance.
Upvotes: 0
Reputation: 311448
The main pain point here probably isn't the ArrayList
, but the use of the +
operator with String
s. Since String
s are immutable in java, each invocation forces the creation of a new object and copying of all the data, which, as you stated, may be quite long.
A faster way to do this would be to use a StringBuilder
, which does not (necessarily) force the copying of the data on each operation:
StringBuilder result = new StringBuilder();
for(String[] classLines: writtenClasses)
{
for(String classLine: classLines)
{
result.append(classLine).append('\n');
}
}
Upvotes: 1
Reputation: 14636
Not a proper answer, but too awkward to read in a comment:
String result = "";
for(String fileLine: processedClasses)
{
result += fileLine + "\n";
}
That is creating a million String instances. I guess using a StringBuilder
here should have a positive effect on performance.
Upvotes: 1