字符串串联是否经过优化以使用现有的字符串生成器?



我有以下代码:

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怎么样?

默认情况下,在java-9中不会有字符串连接StringBuilder;如何通过invokedynamic进行它是运行时决定的。默认策略不是StringBuilder::append策略。

您也可以在此处阅读更多内容。

在java-8下,将创建一个新的(在反编译的字节码中很容易发现两次invokespecial // Method java/lang/StringBuilder."<init>":()V

另外,你有一个关于append.append...的建议;请注意,这比sb.append ... sb.append要好得多,这就是原因。

由于字符串连接优化是由 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>":(,因此每次迭代都会创建新的 StringBuilder(除非即时编译器执行花哨的优化(。

这不太可能是一个重大的性能问题,但在不太可能的情况下,您可以通过重写

str.append("|").append(f);

根据 Java 9 API 文档

实施说明:

字符串连接运算符的实现由 Java 编译器自行决定,只要编译器最终符合 Java™ 语言规范即可。例如,javac 编译器可以使用 StringBuffer、StringBuilder 或 java.lang.invoke.StringConcatFactory 来实现运算符,具体取决于 JDK 版本。字符串转换的实现通常是通过方法toString实现的,该方法由Object定义并由Java中的所有类继承。

根据这一点,它将在您的情况下每次迭代创建一个新的字符串生成器。所以,正如这里几个人提到的,使用以下代码更优化

append("|").append(f)

您可以在此处找到 API 文档

最新更新