我有以下代码:
StringBuilder str = new StringBuilder("foo");
for(Field f : fields){
str.append("|" + f);
}
str.append("|" + bar);
String result = str.toString();
我知道编译器会优化字符串连接"|" + f
并将其替换为StringBuilder。但是,是否会创建新的StringBuilder或在Java 8中使用现有的str
? Java 9怎么样?
答案 0 :(得分:23)
默认情况下在java-9中,字符串连接没有StringBuilder
;它是运行时决定如何通过invokedynamic
进行的。默认策略不是一个StringBuilder::append
。
您还可以阅读更多here。
在java-8下,将创建一个新的(很容易在解编译的字节码中发现两次出现invokespecial // Method java/lang/StringBuilder."<init>":()V
。
此外,您有关于append.append...
的建议;请注意,这比sb.append ... sb.append
要好得多,而here就是原因。
答案 1 :(得分:8)
由于字符串连接优化由Java编译器执行,您可以通过反编译字节代码来查看它的作用:
$ cat Test.java
interface Field {}
public class Test {
static String toString(Field[] fields, Object bar) {
StringBuilder str = new StringBuilder("foo");
for(Field f : fields){
str.append("|" + f);
}
str.append("|" + bar);
return str.toString();
}
}
$ javac Test.java
$ javap -c Test.class
Compiled from "Test.java"
public class stackoverflow.Test {
public stackoverflow.Test();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
static java.lang.String toString(stackoverflow.Field[], java.lang.Object);
Code:
0: new #16 // class java/lang/StringBuilder
3: dup
4: ldc #18 // String foo
6: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
9: astore_2
10: aload_0
11: dup
12: astore 6
14: arraylength
15: istore 5
17: iconst_0
18: istore 4
20: goto 53
23: aload 6
25: iload 4
27: aaload
28: astore_3
29: aload_2
30: new #16 // class java/lang/StringBuilder
33: dup
34: ldc #23 // String |
36: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
39: aload_3
40: invokevirtual #25 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
43: invokevirtual #29 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
46: invokevirtual #32 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: pop
50: iinc 4, 1
53: iload 4
55: iload 5
57: if_icmplt 23
60: aload_2
61: new #16 // class java/lang/StringBuilder
64: dup
65: ldc #23 // String |
67: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
70: aload_1
71: invokevirtual #25 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
74: invokevirtual #29 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
77: invokevirtual #32 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
80: pop
81: aload_2
82: invokevirtual #29 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
85: areturn
}
如您所见,代码在 3个地方中调用StringBuilder构造函数(Method java/lang/StringBuilder."<init>":
),因此将在每次迭代中创建新的StringBuilders(除非实时编译器执行花哨的优化)。
这不太可能是一个重大的性能问题,但在不太可能的情况下,你可以通过重写
轻松解决这个问题。str.append("|").append(f);
答案 2 :(得分:0)
根据Java 9 API文档
实施说明:
只要编译器最终符合Java™语言规范,字符串连接运算符的实现由Java编译器决定。例如,javac编译器可以使用StringBuffer,StringBuilder或java.lang.invoke.StringConcatFactory实现运算符,具体取决于JDK版本。字符串转换的实现通常是通过方法toString,由Object定义并由Java中的所有类继承。
根据这个,它将在你的情况下每次迭代创建一个新的String构建器。所以,正如这里的几个人所提到的,使用下面的代码更加优化
append("|").append(f)
您可以找到API文档here