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