String 拼接字符串效率低?是真的吗?
作者:红袖添香
- 2023-12-05 北京
本文字数:4478 字
阅读完需:约 15 分钟
前言
又刷到这篇文章
网上有很多文章用 JUnit 进行测试验证,可以网上搜一下,这里不做赘述。今天我们从反编译和字节码的角度分析字符串拼接的时候底层到底做了什么
场景
拼接 1:
public class Hello2 {
public static void main(String[] args) {
}
public void add1() {
String a = "aaa" + "bbb" + "ccc";
String b = new StringBuilder("aaa").append("bbb").append("ccc").toString();
}
}
复制代码
反编译结果:
package com.noah.nowcoder;
public class Hello2 {
public static void main(String[] args) {}
public void add1() {
String a = "aaabbbccc";
String b = "aaa" + "bbb" + "ccc";
}
}
复制代码
Java 编译器优化(JDK 版本相关),编译结果可以直接看出, String a = "aaa" + "bbb" + "ccc" 执行效率更高;
拼接 2:
public class Hello {
public static void main(String[] args) {
String str1 = "";
for (int i = 0; i < 100000; i++) {
str1 += "-" + UUID.randomUUID().toString();
}
System.out.println(str1);
StringBuilder stringBuilder = new StringBuilder();
for(int i = 0; i < 100000; ++i) {
stringBuilder.append("-").append(UUID.randomUUID().toString());
}
System.out.println(stringBuilder.toString());
}
}
复制代码
反编译结果:
package com.noah.nowcoder;
import java.util.UUID;
public class Hello {
public static void main(String[] args) {
String str1 = "";
for (int i = 0; i < 100000; i++)
str1 = str1 + "-" + UUID.randomUUID().toString();
System.out.println(str1);
StringBuilder stringBuilder = new StringBuilder();
for (int j = 0; j < 100000; j++)
stringBuilder.append("-").append(UUID.randomUUID().toString());
System.out.println(stringBuilder.toString());
}
}
复制代码
这一步结果不明显
接下来,我们看一下字节码信息
Compiled from "Hello.java"
public class com.noah.nowcoder.Hello {
public com.noah.nowcoder.Hello();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: ldc #3 // int 100000
8: if_icmpge 46
11: new #4 // class java/lang/StringBuilder
14: dup
15: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: ldc #7 // String -
24: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokestatic #8 // Method java/util/UUID.randomUUID:()Ljava/util/UUID;
30: invokevirtual #9 // Method java/util/UUID.toString:()Ljava/lang/String;
33: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
39: astore_1
40: iinc 2, 1
43: goto 5
46: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
49: aload_1
50: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
53: new #4 // class java/lang/StringBuilder
56: dup
57: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
60: astore_2
61: iconst_0
62: istore_3
63: iload_3
64: ldc #3 // int 100000
66: if_icmpge 91
69: aload_2
70: ldc #7 // String -
72: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
75: invokestatic #8 // Method java/util/UUID.randomUUID:()Ljava/util/UUID;
78: invokevirtual #9 // Method java/util/UUID.toString:()Ljava/lang/String;
81: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
84: pop
85: iinc 3, 1
88: goto 63
91: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
94: aload_2
95: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
98: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
101: return
}
复制代码
注意:
15: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
调用了 StringBuilder 构造函数,初始化了一个 StringBuilder 对象(循环中初始化对象)
总结:
根据字节码改写代码
public void add2() {
String str1 = "";
for (int i = 0; i < 100000; i++) {
str1 += "-" + UUID.randomUUID().toString();
}
System.out.println(str1);
}
public void add3() {
String str1 = "";
for (int i = 0; i < 100000; i++) {
StringBuilder stringBuilder = new StringBuilder();
str1 = stringBuilder.append(str1).append("-").append(UUID.randomUUID().toString()).toString();
}
System.out.println(str1);
}
复制代码
字节码文件:
public void add2();
Code:
0: ldc #10 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: ldc #11 // int 100000
8: if_icmpge 46
11: new #3 // class java/lang/StringBuilder
14: dup
15: invokespecial #12 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: ldc #13 // String -
24: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokestatic #14 // Method java/util/UUID.randomUUID:()Ljava/util/UUID;
30: invokevirtual #15 // Method java/util/UUID.toString:()Ljava/lang/String;
33: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
39: astore_1
40: iinc 2, 1
43: goto 5
46: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
49: aload_1
50: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
53: return
复制代码
public void add3();
Code:
0: ldc #10 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: ldc #11 // int 100000
8: if_icmpge 48
11: new #3 // class java/lang/StringBuilder
14: dup
15: invokespecial #12 // Method java/lang/StringBuilder."<init>":()V
18: astore_3
19: aload_3
20: aload_1
21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: ldc #13 // String -
26: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: invokestatic #14 // Method java/util/UUID.randomUUID:()Ljava/util/UUID;
32: invokevirtual #15 // Method java/util/UUID.toString:()Ljava/lang/String;
35: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
38: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
41: astore_1
42: iinc 2, 1
45: goto 5
48: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
51: aload_1
52: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
55: return
复制代码
从字节码文件可以看出,add2() 和 add3() 基本上等价的。
所以循环中拼接字符串更高效的做法是将 StringBulider 放在循环外。
如下:
public static void main(String[] args) {
StringBuilder stringBuilder = new StringBuilder();
for (int j = 0; j < 100000; j++)
stringBuilder.append("-").append(UUID.randomUUID().toString());
System.out.println(stringBuilder.toString());
}
复制代码
划线
评论
复制
发布于: 2023-12-05阅读数: 19
版权声明: 本文为 InfoQ 作者【红袖添香】的原创文章。
原文链接:【http://xie.infoq.cn/article/3daf161f2af4b4dc8159fb0a8】。文章转载请联系作者。
红袖添香
关注
大雨落幽燕,白浪滔天 2018-08-10 加入
还未添加个人简介
评论