假设字符串a和b:
a += b
a = a.concat(b)
在引擎盖下,它们是一样的吗?
这里是concat反编译作为参考。我希望能够反编译+操作符以及看看它做什么。
public String concat(String s) {
int i = s.length();
if (i == 0) {
return this;
}
else {
char ac[] = new char[count + i];
getChars(0, count, ac, 0);
s.getChars(0, i, ac, count);
return new String(0, count + i, ac);
}
}
这里的大多数答案都来自2008年。随着时间的推移,情况似乎发生了变化。我最近用JMH做的基准测试显示,在Java 8 +上比concat快两倍左右。
我的基准:
@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {
@org.openjdk.jmh.annotations.State(Scope.Thread)
public static class State2 {
public String a = "abc";
public String b = "xyz";
}
@org.openjdk.jmh.annotations.State(Scope.Thread)
public static class State3 {
public String a = "abc";
public String b = "xyz";
public String c = "123";
}
@org.openjdk.jmh.annotations.State(Scope.Thread)
public static class State4 {
public String a = "abc";
public String b = "xyz";
public String c = "123";
public String d = "!@#";
}
@Benchmark
public void plus_2(State2 state, Blackhole blackhole) {
blackhole.consume(state.a+state.b);
}
@Benchmark
public void plus_3(State3 state, Blackhole blackhole) {
blackhole.consume(state.a+state.b+state.c);
}
@Benchmark
public void plus_4(State4 state, Blackhole blackhole) {
blackhole.consume(state.a+state.b+state.c+state.d);
}
@Benchmark
public void stringbuilder_2(State2 state, Blackhole blackhole) {
blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
}
@Benchmark
public void stringbuilder_3(State3 state, Blackhole blackhole) {
blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
}
@Benchmark
public void stringbuilder_4(State4 state, Blackhole blackhole) {
blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
}
@Benchmark
public void concat_2(State2 state, Blackhole blackhole) {
blackhole.consume(state.a.concat(state.b));
}
@Benchmark
public void concat_3(State3 state, Blackhole blackhole) {
blackhole.consume(state.a.concat(state.b.concat(state.c)));
}
@Benchmark
public void concat_4(State4 state, Blackhole blackhole) {
blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
}
}
结果:
Benchmark Mode Cnt Score Error Units
StringConcatenation.concat_2 thrpt 50 24908871.258 ± 1011269.986 ops/s
StringConcatenation.concat_3 thrpt 50 14228193.918 ± 466892.616 ops/s
StringConcatenation.concat_4 thrpt 50 9845069.776 ± 350532.591 ops/s
StringConcatenation.plus_2 thrpt 50 38999662.292 ± 8107397.316 ops/s
StringConcatenation.plus_3 thrpt 50 34985722.222 ± 5442660.250 ops/s
StringConcatenation.plus_4 thrpt 50 31910376.337 ± 2861001.162 ops/s
StringConcatenation.stringbuilder_2 thrpt 50 40472888.230 ± 9011210.632 ops/s
StringConcatenation.stringbuilder_3 thrpt 50 33902151.616 ± 5449026.680 ops/s
StringConcatenation.stringbuilder_4 thrpt 50 29220479.267 ± 3435315.681 ops/s
基本上,+和concat方法之间有两个重要的区别。
If you are using the concat method then you would only be able to concatenate strings while in case of the + operator, you can also concatenate the string with any data type.
For Example:
String s = 10 + "Hello";
In this case, the output should be 10Hello.
String s = "I";
String s1 = s.concat("am").concat("good").concat("boy");
System.out.println(s1);
In the above case you have to provide two strings mandatory.
The second and main difference between + and concat is that:
Case 1:
Suppose I concat the same strings with concat operator in this way
String s="I";
String s1=s.concat("am").concat("good").concat("boy");
System.out.println(s1);
In this case total number of objects created in the pool are 7 like this:
I
am
good
boy
Iam
Iamgood
Iamgoodboy
Case 2:
Now I am going to concatinate the same strings via + operator
String s="I"+"am"+"good"+"boy";
System.out.println(s);
In the above case total number of objects created are only 5.
Actually when we concatinate the strings via + operator then it maintains a StringBuffer class to perform the same task as follows:-
StringBuffer sb = new StringBuffer("I");
sb.append("am");
sb.append("good");
sb.append("boy");
System.out.println(sb);
In this way it will create only five objects.
伙计们,这就是+和concat方法的基本区别。
享受:)
我运行了类似于@marcio的测试,但使用了以下循环:
String c = a;
for (long i = 0; i < 100000L; i++) {
c = c.concat(b); // make sure javac cannot skip the loop
// using c += b for the alternative
}
为了更好的度量,我还添加了StringBuilder.append()。每个测试运行10次,每次运行100,000次。以下是调查结果:
StringBuilder轻松获胜。大多数运行的时钟时间结果为0,最长的运行时间为16毫秒。
A += b每次运行大约需要40000ms (40s)。
Concat每次运行只需要10000ms (10s)。
我还没有反编译类来查看内部结构或通过分析器运行它,但我怀疑a += b花了很多时间来创建StringBuilder的新对象,然后将它们转换回String。
Tom准确地描述了+运算符的作用。它创建了一个临时的StringBuilder,添加了部分,并以toString()结束。
然而,到目前为止,所有的答案都忽略了HotSpot运行时优化的影响。具体来说,这些临时操作被认为是一种公共模式,并在运行时被更有效的机器代码所取代。
@marcio:你创建了一个微基准测试;在现代JVM中,这不是一种分析代码的有效方法。
运行时优化之所以重要,是因为一旦HotSpot开始运行,代码中的许多差异(甚至包括对象创建)就完全不同了。唯一确定的方法是在原位分析您的代码。
最后,所有这些方法实际上都非常快。这可能是一个过早优化的例子。如果您的代码连接了很多字符串,那么获得最大速度的方法可能与您选择的操作符无关,而是与您使用的算法有关!
为了完整起见,我想补充一下,'+'操作符的定义可以在JLS SE8 15.18.1中找到:
如果只有一个操作数表达式为String类型,则String
在另一个操作数上执行转换(§5.1.11)以生成一个
字符串。
字符串连接的结果是对string对象的引用
这是两个操作数字符串的连接。的字符
在右操作数字符的前面
新创建的字符串中的操作数。
String对象是新创建的(§12.5),除非表达式是
常量表达式(§15.28)
关于实现,JLS说了以下几点:
An implementation may choose to perform conversion and concatenation
in one step to avoid creating and then discarding an intermediate
String object. To increase the performance of repeated string
concatenation, a Java compiler may use the StringBuffer class or a
similar technique to reduce the number of intermediate String objects
that are created by evaluation of an expression.
For primitive types, an implementation may also optimize away the
creation of a wrapper object by converting directly from a primitive
type to a string.
因此,从“Java编译器可能使用StringBuffer类或类似的技术来减少”判断,不同的编译器可以产生不同的字节码。