String串联是否经过优化以使用现有的StringBuilders?

问题描述 投票:22回答:3

我有以下代码:

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 performance java-8 java-9
3个回答
23
投票

默认情况下,java-9中没有用于字符串连接的StringBuilder;它是运行时决定如何通过q​​azxswpoi制作的。默认政策不是invokedynamic政策。

您还可以阅读更多StringBuilder::append

在java-8下,将创建一个新的(很容易在解编译的字节码中发现两次出现的here)。

另外,你有关于invokespecial // Method java/lang/StringBuilder."<init>":()V的建议;只是注意到这比append.append...要好得多,而sb.append ... sb.append就是为什么。


8
投票

由于字符串连接优化由Java编译器执行,您可以通过反编译字节代码来查看它的作用:

here

如您所见,代码在3个位置调用StringBuilder构造函数($ 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 } ),因此将在每次迭代中创建新的StringBuilders(除非实时编译器执行花式优化)。

这不太可能是一个重大的性能问题,但在不太可能的情况下,你可以通过重写来轻松解决这个问题

Method java/lang/StringBuilder."<init>":

0
投票

根据Java 9 API文档

实施说明:

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

根据这个,它会在你的情况下每次迭代创建一个新的String构建器。因此,正如这里的几个人所提到的,使用下面的代码更加优化

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

您可以找到API文档append("|").append(f)

© www.soinside.com 2019 - 2024. All rights reserved.