写点什么

String 拼接字符串效率低?是真的吗?

作者:红袖添香
  • 2023-12-05
    北京
  • 本文字数:4478 字

    阅读完需:约 15 分钟

String 拼接字符串效率低?是真的吗?

前言

又刷到这篇文章



网上有很多文章用 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
用户头像

红袖添香

关注

大雨落幽燕,白浪滔天 2018-08-10 加入

还未添加个人简介

评论

发布
暂无评论
String 拼接字符串效率低?是真的吗?_Java_红袖添香_InfoQ写作社区