从java反编译及字节码角度探索分析String拼接字符串效率

 更新时间:2023年12月11日 10:29:57   作者:红袖添香  
这篇文章主要介绍了从java反编译及字节码角度探索分析String拼接字符串效率,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

前言

又刷到这篇文章

为什么idea建议去掉StringBuilder,使用“+”拼接字符串

网上有很多文章用 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 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());
  }

以上就是从java反编译及字节码角度探索分析String拼接字符串效率的详细内容,更多关于java String拼接字符串效率的资料请关注脚本之家其它相关文章!

相关文章

  • Java线程实现的三种方式详细解析

    Java线程实现的三种方式详细解析

    这篇文章主要介绍了Java线程实现的三种方式详细解析,Java多线程实现方式主要有三种,继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程,需要的朋友可以参考下
    2023-12-12
  • 详解Java Socket通信封装MIna框架

    详解Java Socket通信封装MIna框架

    Mina异步IO使用的Java底层JNI框架,Mina提供服务端和客户端,将我们的业务解耦开发,真正做到高内聚低耦合的思想。
    2021-06-06
  • 基于SpringMVC接受JSON参数详解及常见错误总结

    基于SpringMVC接受JSON参数详解及常见错误总结

    下面小编就为大家分享一篇基于SpringMVC接受JSON参数详解及常见错误总结,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • Java实现颜色渐变效果

    Java实现颜色渐变效果

    这篇文章主要为大家详细介绍了Java实现颜色渐变效果的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • DynamicDataSource怎样解决多数据源的事务问题

    DynamicDataSource怎样解决多数据源的事务问题

    这篇文章主要介绍了DynamicDataSource怎样解决多数据源的事务问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • 基于maven install 没反应的解决方法

    基于maven install 没反应的解决方法

    下面小编就为大家带来一篇基于maven install 没反应的解决方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • Debian 7 和 Debian 8 用户安装 Java 8的方法

    Debian 7 和 Debian 8 用户安装 Java 8的方法

    Oracle Java 8 稳定版本近期已发布,有很多新的特征变化。其中,有功能的程序支持通过“Lambda项目 ”,收到了一些安全更新和界面改进上的bug修复,使得开发人员的工作更容易。
    2014-03-03
  • Mac M1安装JDK的实战避坑指南

    Mac M1安装JDK的实战避坑指南

    这篇文章主要给大家介绍了关于Mac M1安装JDK避坑的相关资料,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-02-02
  • springboot实现将自定义日志格式存储到mongodb中

    springboot实现将自定义日志格式存储到mongodb中

    这篇文章主要介绍了springboot实现将自定义日志格式存储到mongodb中的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 使用feign发送http请求解析报错的问题

    使用feign发送http请求解析报错的问题

    这篇文章主要介绍了使用feign发送http请求解析报错的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03

最新评论