java中StringBuilder与加号拼接字符串的效率分析

标签: java String
11人阅读 评论(0) 收藏 举报

案例1:拼接字符串常量

    String s1 = "a" + "b" + "c";
    String s2 = new StringBuilder().append("a").append("b").append("c").toString();

反编译class文件

    String s1 = "abc";
    String s2 = "a" + "b" + "c";

Byte Code

     0  ldc <String "abc"> [21]
     2  astore_1 [s1]
     3  new java.lang.StringBuilder [23]
     6  dup
     7  invokespecial java.lang.StringBuilder() [25]
    10  ldc <String "a"> [26]
    12  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [28]
    15  ldc <String "b"> [32]
    17  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [28]
    20  ldc <String "c"> [34]
    22  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [28]
    25  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [36]
    28  astore_2 [s2]

分析:

    可以看出,使用 加号 进行字符串常量的拼接在编译时就已经完成,而使用 StringBuilder 进行字符串拼接需要在运行时完成。所以单纯的字符串常量拼接 加号 的效率 应该高于 StringBuilder,下面我们进行测试:

    long l1 = System.currentTimeMillis();
    for (int i=0; i<1_000_000; i++) {
        String s1 = "a" + "b" + "c";
    }
    System.out.println(System.currentTimeMillis() - l1);    //结果: 2
    
    long l2 = System.currentTimeMillis();
    for (int i=0; i<1_000_000; i++) {
        String s2 = new StringBuilder().append("a").append("b").append("c").toString();
    }
    System.out.println(System.currentTimeMillis() - l2);    //结果 : 19
    测试结果与预测一致。

案例2:拼接字符串与引用

    String s1 = "a";
    String s2 = new StringBuilder().append(s1).append("b").append("c").toString();
    String s3 = s1 + "b" + "c";

反编译class文件

    String s1 = "a";
    String s2 = s1 + "b" + "c";
    String s3 = s1 + "b" + "c";

Byte Code

     0  ldc <String "a"> [16]
     2  astore_1 [s1]
     3  new java.lang.StringBuilder [24]
     6  dup
     7  invokespecial java.lang.StringBuilder() [26]
    10  aload_1 [s1]
    11  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [27]
    14  ldc <String "b"> [31]
    16  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [27]
    19  ldc <String "c"> [33]
    21  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [27]
    24  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [35]
    27  astore_2 [s2]
    28  new java.lang.StringBuilder [24]
    31  dup
    32  aload_1 [s1]
    33  invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [50]
    36  invokespecial java.lang.StringBuilder(java.lang.String) [56]
    39  ldc <String "b"> [31]
    41  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [27]
    44  ldc <String "c"> [33]
    46  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [27]
    49  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [35]
    52  astore_3 [s3]

分析:

    从反编译的结果来看两种方式似乎一样,但字节码有细微的不同(11行和33行),虽然都是创建 StringBuilder 对象进行拼接,但加号拼接的方式多做了一次类型转换。所以 StringBuilder的方式应该更高效,下面是测试结果:

	String s1 = "a";
	long l1 = System.currentTimeMillis();
	for (int i=0; i<1_000_000; i++) {
		String s2 = new StringBuilder().append(s1).append("b").append("c").toString();
	}
	System.out.println(System.currentTimeMillis() - l1);	//结果: 29
	
	long l2 = System.currentTimeMillis();
	for (int i=0; i<1_000_000; i++) {
		String s3 = s1 + "b" + "c";
	}
	System.out.println(System.currentTimeMillis() - l2);	//结果 : 48

测试结果与预期一致。

案例3:循环拼接字符串

    通常我们都会对一个对象进行字符串的循环拼接,例如:

	String s1 = "";
	long l1 = System.currentTimeMillis();
	for(int i=0; i<100_000; i++) {
		s1 = s1 + "a" + "b" + "c" + "d" + "e" + "f";
	}
	System.out.println((System.currentTimeMillis() - l1) + " " + s1.length());  //结果 15041 600000
	
	StringBuilder sb = new StringBuilder();
	String s2;
	long l2 = System.currentTimeMillis();
	for(int i=0; i<100_000; i++) {
		sb.append("a").append("b").append("c").append("d").append("e").append("f");
	}
	s2 = sb.toString();
	System.out.println((System.currentTimeMillis() - l2) + " " + s2.length());  //结果 6 600000
反编译classes
    String s1 = "";
    long l1 = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
      s1 = s1 + "a" + "b" + "c" + "d" + "e" + "f";
    }
    System.out.println(System.currentTimeMillis() - l1 + " " + s1.length());
    
    StringBuilder sb = new StringBuilder();
    
    long l2 = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
      sb.append("a").append("b").append("c").append("d").append("e").append("f");
    }
    String s2 = sb.toString();
    System.out.println(System.currentTimeMillis() - l2 + " " + s2.length());

Byte Code

      0  ldc <String ""> [16]
      2  astore_1 [s1]
      3  invokestatic java.lang.System.currentTimeMillis() : long [18]
      6  lstore_2 [l1]
      7  iconst_0
      8  istore 4 [i]
     10  goto 61
     13  new java.lang.StringBuilder [24]
     16  dup
     17  aload_1 [s1]
     18  invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [26]
     21  invokespecial java.lang.StringBuilder(java.lang.String) [32]
     24  ldc <String "a"> [35]
     26  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
     29  ldc <String "b"> [41]
     31  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
     34  ldc <String "c"> [43]
     36  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
     39  ldc <String "d"> [45]
     41  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
     44  ldc <String "e"> [47]
     46  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
     49  ldc <String "f"> [49]
     51  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
     54  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [51]
     57  astore_1 [s1]
     58  iinc 4 1 [i]
     61  iload 4 [i]
     63  ldc <Integer 100000> [55]
     65  if_icmplt 13
     68  getstatic java.lang.System.out : java.io.PrintStream [56]
     71  new java.lang.StringBuilder [24]
     74  dup
     75  invokestatic java.lang.System.currentTimeMillis() : long [18]
     78  lload_2 [l1]
     79  lsub
     80  invokestatic java.lang.String.valueOf(long) : java.lang.String [60]
     83  invokespecial java.lang.StringBuilder(java.lang.String) [32]
     86  ldc <String " "> [63]
     88  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
     91  aload_1 [s1]
     92  invokevirtual java.lang.String.length() : int [65]
     95  invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [69]
     98  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [51]
    101  invokevirtual java.io.PrintStream.println(java.lang.String) : void [72]
    104  new java.lang.StringBuilder [24]
    107  dup
    108  invokespecial java.lang.StringBuilder() [77]
    111  astore 4 [sb]
    113  invokestatic java.lang.System.currentTimeMillis() : long [18]
    116  lstore 6 [l2]
    118  iconst_0
    119  istore 8 [i]
    121  goto 160
    124  aload 4 [sb]
    126  ldc <String "a"> [35]
    128  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
    131  ldc <String "b"> [41]
    133  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
    136  ldc <String "c"> [43]
    138  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
    141  ldc <String "d"> [45]
    143  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
    146  ldc <String "e"> [47]
    148  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
    151  ldc <String "f"> [49]
    153  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
    156  pop
    157  iinc 8 1 [i]
    160  iload 8 [i]
    162  ldc <Integer 100000> [55]
    164  if_icmplt 124
    167  aload 4 [sb]
    169  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [51]
    172  astore 5 [s2]
    174  getstatic java.lang.System.out : java.io.PrintStream [56]
    177  new java.lang.StringBuilder [24]
    180  dup
    181  invokestatic java.lang.System.currentTimeMillis() : long [18]
    184  lload 6 [l2]
    186  lsub
    187  invokestatic java.lang.String.valueOf(long) : java.lang.String [60]
    190  invokespecial java.lang.StringBuilder(java.lang.String) [32]
    193  ldc <String " "> [63]
    195  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
    198  aload 5 [s2]
    200  invokevirtual java.lang.String.length() : int [65]
    203  invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [69]
    206  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [51]
    209  invokevirtual java.io.PrintStream.println(java.lang.String) : void [72]

分析:

    测试结果 StringBuilder拼接字符串 的效率 远远高于 加号拼接字符串,主要原因在于 加号拼接字符串的方式 每次循环开始时都会创建一个 StringBuilder实例(Byte Code 65行与13行 ps: if_icmplt 条件分支指令 如果一个int类型值小于另外一个int类型值,则跳转),而 StringBuilder拼接字符串 的方式只在循环开始前创建了一个 StringBuilder实例。





查看评论

在Java中连接字符串时是使用+号还是使用StringBuilder StringBuffer 加号

字符串是Java程序中最常用的一种数据结构之一。在Java中的String类已经重载的"+"。也就是说,字符串可以直接使用"+"进行连接,如下面代码所示: String s = "abc" ...
  • luyan1992
  • luyan1992
  • 2016-03-24 17:03:24
  • 4586

java中String,StringBuffer,StringBuilder拼接效率对比

比较JAVA中String ,StringBuffer,SrtingBuilder三个对象连接字符串的效率。 我们经常都听有经验的人说,避免使用String通过“+”连接字符串,特 ...
  • liu136313
  • liu136313
  • 2016-03-10 23:48:18
  • 4274

String+,StringBuilder,String.format运行效率比较

实现String字符串相加的方法有很多,常见的有直接相加
  • xianymo
  • xianymo
  • 2014-10-29 11:03:02
  • 4591

Java 8中不再需要StringBuilder拼接字符串

在Java开发者中,字符串的拼接占用资源高往往是热议的话题. 让我们深入讨论一下为什么会占用高资源。 在Java中,字符串对象是不可变的,意思是它一旦创建,你就无法再改变它。所以在我们拼接...
  • hj7jay
  • hj7jay
  • 2016-12-28 09:52:02
  • 4004

Java 字符串拼接效率比较

字符串拼接的三种方法 ① 加号 ② concat方法 ③ StringBuilder(或StringBuffer)的append方法2 程序例子package...
  • Zen99T
  • Zen99T
  • 2016-04-26 23:33:13
  • 7193

java 字符串拼接为什么要用 StringBuilder 而不直接用 String 相加连接

经常在网上看到或者在周围听到有人说字符串拼接不要直接用 String 相加, StringBuilder 的效率要比 String 直接相加拼接要高。还有人常说, StringBuffer 是同步的(...
  • shfqbluestone
  • shfqbluestone
  • 2014-06-24 23:39:09
  • 7489

stringBuilder - 解决拼接字符串产生的滞留性

/* * 学习stringBuilder:目的解决拼接字符串产生的滞留性 * string类有不可改变性,每次执行字符串拼接的时候,实际上都会产生新的字符串对象 * stringBuilder类...
  • Memoryuuu
  • Memoryuuu
  • 2016-12-14 22:39:50
  • 134

StringBuilder.append 中出现加号影响效率

。。。
  • Asmots
  • Asmots
  • 2015-03-17 11:28:54
  • 444

[JAVA]浅谈String, StringBuilder字符串拼接速度

[JAVA] String, StringBuilder 从编译器角度分析String和StringBuilder做字符串拼接时的速度...
  • u013882229
  • u013882229
  • 2017-10-05 21:15:18
  • 367

String中的加号和字符串拼接的理解

直接看代码 String str1 = new String("abc"); String str2 = "abc"; Str...
  • weililansehudiefei
  • weililansehudiefei
  • 2017-05-05 19:08:21
  • 1807
    个人资料
    等级:
    访问量: 1255
    积分: 53
    排名: 167万+
    文章分类
    文章存档