Reputation: 7374
At first step I run this code:
public class Demo {
public static void main(String[] args) {
String x = "x";
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
{
x = x.concat("s");
// x+="k";
}
System.out.println(System.currentTimeMillis() - start);
}
}
Out: 13579.
On second step I run this code:
public class Demo {
public static void main(String[] args) {
String x = "x";
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
{
//x = x.concat("s");
x+="k";
}
System.out.println(System.currentTimeMillis() - start);
}
}
Out: 27328.
And I have two questions:
Upvotes: 3
Views: 402
Reputation: 2017
If you single-step through a line such as
String x2 = x + "x";
in Eclipse you will see that it creates a StringBuilder
object to help construct the new String
. That is what is
happening in your second case.
In the first case, String.concat
creates a new String
directly without
any StringBuilder
so it is a bit faster.
Upvotes: 0
Reputation: 137567
Does it help if you know that these are equivalent:
x+="k";
x = new StringBuffer(x).append("k").toString();
Or in newer Java (I forget whether it is from 1.5 or 1.6) it uses this instead, as it at least avoids the locking that StringBuffer
had inflicted upon it:
x = new StringBuilder(x).append("k").toString();
With all that memory management going on, it's no wonder that the concat(String)
method can do better. On the other hand, if you were going to be doing that loop, you'd be better off not converting back and forth between mutable objects and immutable ones so much:
String x = "x";
long start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder(x);
for (int i = 0; i < 100000; i++) {
sb.append("f");
}
x = sb.toString();
System.out.println(System.currentTimeMillis() - start);
Since StringBuilder
(and StringBuffer
too as it happens, but that's less efficient for other reasons) does intelligent buffer management for you, that's going to be a significantly faster technique; going even faster than that would require real effort (or at least pre-sizing the buffer, which is possible in this case but harder in general).
Upvotes: 0
Reputation: 500247
I think that your microbenchmark is fine, and I can reproduce your result.
On my JVM, the reason x += "k"
is twice as slow is that under the covers it does the following:
StringBuilder
;x
to the StringBuilder
;"k"
to the StringBuilder
;StringBuilder.toString()
and assign the result to x
.This copies the character data twice (once in step 2 and once in step 4).
On the other hand, x = x.concat("s")
only copies the data once.
This double copying makes x += "k"
two times slower than the other version.
If you're curious, here are the bytecodes that my compiler has generated for the +=
loop:
10: goto 36
13: new #24; //class java/lang/StringBuilder
16: dup
17: aload_1
18: invokestatic #26; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
21: invokespecial #32; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
24: ldc #35; //String k
26: invokevirtual #37; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: invokevirtual #41; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_1
33: iinc 4, 1
36: iload 4
38: ldc #45; //int 100000
40: if_icmplt 13
Instructions 21 & 29 are where the two copies are made.
Upvotes: 6
Reputation: 298838
Now try this one, it will beat both of yours:
public class Demo {
public static void main(String[] args) {
StringBuilder x = new StringBuilder("x");
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
{
x.append("k");
}
System.out.println(System.currentTimeMillis() - start);
}
}
And here's a slightly optimized version:
public class Demo {
public static void main(String[] args) {
StringBuilder x = new StringBuilder(100001).append('x');
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++)
{
x.append('k');
}
System.out.println(System.currentTimeMillis() - start);
}
}
Upvotes: 0
Reputation: 41510
Because when you concatenate strings using +
, you actually create a new StringBuffer and work with it instead of the original string, which is why this eventually may get slower than when you only use concat.
Regarding correctness, it is best to have a look at bytecode to see what is actually is going on. Compilers may optimize some code away if they know the exact result of its execution, and it never changes.
Upvotes: 0