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:
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.
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.
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);
}
}
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).
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.