String:

  1,是字符串常量,一旦创建就不能修改。对于已经存在了的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去。

  2,String也是final类,不能被继承。

  3,而且String是对象而不是基本类型。

  4,string覆盖了equals方法和hashCode()方法。

StingBuffer:

  1,是字符串可变对象,可以对字符串进行操作,修改字符串原有值时不会新建一个对象。

  2,执行效率较慢,但是线程安全

  3,StringBuffer没有覆盖equals方法和hashCode()方法。

  4,可以动态的构造字符数据,append()方法。

StringBuilder:

  1,也是字符串可变对象,同StringBuffer一样,可以对字符串进行操作,也不会新建对象。

  2,执行效率高效,但是线程不安全。

Java中测试StringBuilder、StringBuffer、String在字符串拼接上的性能

应一个大量字符串拼接的任务

测试一下StringBuilder、StringBuffer、String在操作字符串拼接时候的性能

性能上理论是StringBuilder  >  StringBuffer  >  String

测试方法:

创建一个StirngTest类含有静态常量字符串是26个字母,以及一个循环轮数

在测试时采用循环来操作字符串的拼接,计算操作时间并输出:

package com.test.xlc;

import java.util.Random;

public class StringTest {

    static final String strFinal = "abcdefghijklmnopqrstovwxyz";
    static final int forInt = 100;

    static void StringBuilderTest() {

        Random rand = new Random(47);
        StringBuilder strB = new StringBuilder();
        long start = System.currentTimeMillis();
        for(int i = 0; i < forInt; ++i) {
            int randInt = rand.nextInt(23);
            strB.append(strFinal.substring(randInt, randInt + 4));
        }
        long end = System.currentTimeMillis();
//        System.out.println(strB.toString());
        System.out.println("StringBuilderTest:" + (end - start));
    }

    static void StringBufferTest() {
        Random rand = new Random(47);
        StringBuffer strB = new StringBuffer();
        long start = System.currentTimeMillis();
        for(int i = 0; i < forInt; ++i) {
            int randInt = rand.nextInt(23);
            strB.append(strFinal.substring(randInt, randInt + 4));
        }
        long end = System.currentTimeMillis();
//        System.out.println(strB.toString());
        System.out.println("StringBufferTest:" + (end - start));
    }

    static void StringOnly() {
        Random rand = new Random(47);
        String str = "";
        long start = System.currentTimeMillis();
        for(int i = 0; i < forInt; ++i) {
            int randInt = rand.nextInt(23);
            str += strFinal.substring(randInt, randInt + 4);
        }
        long end = System.currentTimeMillis();
//        System.out.println(str);
        System.out.println("String:" + (end - start));
    }


    public static void main(String...args) {
        StringBuilderTest();
        StringBufferTest();
        StringOnly();

    }
}

测试循环次数:

100

StringBuilderTest:0
StringBufferTest:0
String:0

1000

StringBuilderTest:1
StringBufferTest:0
String:7

10000

StringBuilderTest:2
StringBufferTest:2
String:217

100000

StringBuilderTest:12
StringBufferTest:10
String:17136

1000000

StringBuilderTest:55
StringBufferTest:53
我提前终止了

之后我又测试了拼接长串,本实验结果显示在拼接字符串的效率上:

StringBuffer  >    StringBuilder  >  String

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

java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

案例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 t1 = "a";
    String t2 = t1 + "b" + "c";
    String t3 = t1 + "bc";

Byte Code

L1
    LINENUMBER 6 L1
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "b"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "c"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 2
   L2
    LINENUMBER 7 L2
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "bc"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 3
L3

分析:

    从反编译的结果可以看出加号拼接的方式在编译过程中完成了"b"、"c"的拼接,而创建 StringBuilder 对象进行拼接需要在运行中进行"b"、"c"的拼接,加号拼接的方式应该更高效,下面是测试结果:

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); //结果: 66

long l2 = System.currentTimeMillis();
for (int i=0; i<1_000_000; i++) {
String s3 = s1 + "b" + "c";
}
System.out.println(System.currentTimeMillis() - l2);//结果 : 41

测试结果与预期一致。

案例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实例。



01-05 05:07