看到网上有人已经做过对比,并且贴出了代码,然后我运行了之后发现跟我分析的结论差距很大。发现他的代码有个问题,UUID.randomUUID() 首次调用耗时会很高,这个耗时被计算给了String,这对String是不公平的。
原始代码参见:http://www.codes51.com/article/detail_99554.html
修改后的测试代码如下:
importjava.util.Date;importjava.util.UUID;public classStringTest {public static voidmain(String[] args) {int testLength = 10000;
String[] arr= new String[2];
String str= "";
Date start= newDate();
String testStr=UUID.randomUUID().toString();
System.out.println("首次生成randomUUID耗时:" + (new Date().getTime() -start.getTime()));
start= newDate();for (int i = 0; i < testLength; i++) {
testStr=UUID.randomUUID().toString();
}
System.out.println("非首次生成randomUUID " + testLength + "次耗时:" + (new Date().getTime() -start.getTime()));
start= newDate();for (int i = 0; i < testLength; i++) {
str= testStr +testStr;
}
System.out.println("String 拼接测试,测试长度" + testLength + ",测试字符串数组长度" + arr.length + ",完成时间" + (new Date().getTime() -start.getTime()));
start= newDate();for (int i = 0; i < testLength; i++) {
str=testStr.concat(testStr);
}
System.out.println("String.concat 拼接测试,测试长度" + testLength + ",测试字符串数组长度" + arr.length + ",完成时间" + (new Date().getTime() -start.getTime()));
start= newDate();
StringBuilder sb;for (int i = 0; i < testLength; i++) {
str= "";
sb= newStringBuilder();for (int j = 0; j < arr.length; j++) {
sb.append(testStr);
}
str=sb.toString();
}
System.out.println("StringBuilder 拼接测试,测试长度" + testLength + ",测试字符串数组长度" + arr.length + ",完成时间" + (new Date().getTime() -start.getTime()));
}
}
测试结果:
1. 测试字符串数组长度10
首次生成randomUUID耗时:290
非首次生成randomUUID 10000次耗时:44
String 拼接测试,测试长度10000,测试字符串数组长度10,完成时间14
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度10,完成时间66
String.concat 拼接测试,测试长度10000,测试字符串数组长度10,完成时间14
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度10,完成时间14
2. 测试字符串数组长度5
首次生成randomUUID耗时:287
非首次生成randomUUID 10000次耗时:48
String 拼接测试,测试长度10000,测试字符串数组长度5,完成时间11
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度5,完成时间20
String.concat 拼接测试,测试长度10000,测试字符串数组长度5,完成时间9
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度5,完成时间10
3. 测试字符串数组长度3
首次生成randomUUID耗时:308
非首次生成randomUUID 10000次耗时:35
String 拼接测试,测试长度10000,测试字符串数组长度3,完成时间10
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度3,完成时间21
String.concat 拼接测试,测试长度10000,测试字符串数组长度3,完成时间6
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度3,完成时间11
4. 测试字符串数组长度2
首次生成randomUUID耗时:298
非首次生成randomUUID 10000次耗时:70
String 拼接测试,测试长度10000,测试字符串数组长度2,完成时间10
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度2,完成时间8
String.concat 拼接测试,测试长度10000,测试字符串数组长度2,完成时间3
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度2,完成时间7
5. 测试字符串数组长度1
首次生成randomUUID耗时:278
非首次生成randomUUID 10000次耗时:71
String 拼接测试,测试长度10000,测试字符串数组长度1,完成时间1
String 使用循环 拼接测试,测试长度10000,测试字符串数组长度1,完成时间8
String.concat 拼接测试,测试长度10000,测试字符串数组长度1,完成时间3
StringBuilder 拼接测试,测试长度10000,测试字符串数组长度1,完成时间4
到此,可以看出,绝大多数情况下StringBuilder妥妥的比String 使用循环快,但是跟String直接相加差不多,String concat效率跟StringBuilder差不多,很多时候还要快一些,这些都是为什么呢?
javap -c StringTest.class 看看Java编译器都做了什么:
Compiled from "StringTest.java"
public classStringTest {publicStringTest();
Code:0: aload_01: invokespecial #1 //Method java/lang/Object."":()V
4: return
public static voidmain(java.lang.String[]);
Code:0: sipush 10000
3: istore_14: iconst_25: anewarray #2 //class java/lang/String
8: astore_29: ldc #3 //String
11: astore_312: new #4 //class java/util/Date
15: dup16: invokespecial #5 //Method java/util/Date."":()V
19: astore 4
21: invokestatic #6 //Method java/util/UUID.randomUUID:()Ljava/util/UUID;
24: invokevirtual #7 //Method java/util/UUID.toString:()Ljava/lang/String;
27: astore 5
29: getstatic #8 //Field java/lang/System.out:Ljava/io/PrintStream;
32: new #9 //class java/lang/StringBuilder
35: dup36: invokespecial #10 //Method java/lang/StringBuilder."":()V
39: ldc #11 //String 首次生成randomUUID耗时:
41: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
44: new #4 //class java/util/Date
47: dup48: invokespecial #5 //Method java/util/Date."":()V
51: invokevirtual #13 //Method java/util/Date.getTime:()J
54: aload 4
56: invokevirtual #13 //Method java/util/Date.getTime:()J
59: lsub60: invokevirtual #14 //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
63: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
66: invokevirtual #16 //Method java/io/PrintStream.println:(Ljava/lang/String;)V
69: new #4 //class java/util/Date
72: dup73: invokespecial #5 //Method java/util/Date."":()V
76: astore 4
78: iconst_079: istore 6
81: iload 6
83: iload_184: if_icmpge 101
87: invokestatic #6 //Method java/util/UUID.randomUUID:()Ljava/util/UUID;
90: invokevirtual #7 //Method java/util/UUID.toString:()Ljava/lang/String;
93: astore 5
95: iinc 6, 1
98: goto 81
101: getstatic #8 //Field java/lang/System.out:Ljava/io/PrintStream;
104: new #9 //class java/lang/StringBuilder
107: dup108: invokespecial #10 //Method java/lang/StringBuilder."":()V
111: ldc #17 //String 非首次生成randomUUID
113: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
116: iload_1117: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
120: ldc #19 //String 次耗时:
122: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
125: new #4 //class java/util/Date
128: dup129: invokespecial #5 //Method java/util/Date."":()V
132: invokevirtual #13 //Method java/util/Date.getTime:()J
135: aload 4
137: invokevirtual #13 //Method java/util/Date.getTime:()J
140: lsub141: invokevirtual #14 //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
144: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
147: invokevirtual #16 //Method java/io/PrintStream.println:(Ljava/lang/String;)V
150: new #4 //class java/util/Date
153: dup154: invokespecial #5 //Method java/util/Date."":()V
157: astore 4
159: iconst_0160: istore 6
162: iload 6
164: iload_1165: if_icmpge 195
168: new #9 //class java/lang/StringBuilder
171: dup172: invokespecial #10 //Method java/lang/StringBuilder."":()V
175: aload 5
177: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
180: aload 5
182: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
185: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
188: astore_3189: iinc 6, 1
192: goto 162
195: getstatic #8 //Field java/lang/System.out:Ljava/io/PrintStream;
198: new #9 //class java/lang/StringBuilder
201: dup202: invokespecial #10 //Method java/lang/StringBuilder."":()V
205: ldc #20 //String String 拼接测试,测试长度
207: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
210: iload_1211: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
214: ldc #21 //String ,测试字符串数组长度
216: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
219: aload_2220: arraylength221: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
224: ldc #22 //String ,完成时间
226: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
229: new #4 //class java/util/Date
232: dup233: invokespecial #5 //Method java/util/Date."":()V
236: invokevirtual #13 //Method java/util/Date.getTime:()J
239: aload 4
241: invokevirtual #13 //Method java/util/Date.getTime:()J
244: lsub245: invokevirtual #14 //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
248: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
251: invokevirtual #16 //Method java/io/PrintStream.println:(Ljava/lang/String;)V
254: new #4 //class java/util/Date
257: dup258: invokespecial #5 //Method java/util/Date."":()V
261: astore 4
263: iconst_0264: istore 6
266: iload 6
268: iload_1269: if_icmpge 317
272: ldc #3 //String
274: astore_3275: iconst_0276: istore 7
278: iload 7
280: aload_2281: arraylength282: if_icmpge 311
285: new #9 //class java/lang/StringBuilder
288: dup289: invokespecial #10 //Method java/lang/StringBuilder."":()V
292: aload_3293: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
296: aload 5
298: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
301: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
304: astore_3305: iinc 7, 1
308: goto 278
311: iinc 6, 1
314: goto 266
317: getstatic #8 //Field java/lang/System.out:Ljava/io/PrintStream;
320: new #9 //class java/lang/StringBuilder
323: dup324: invokespecial #10 //Method java/lang/StringBuilder."":()V
327: ldc #23 //String String 使用循环 拼接测试,测试长度
329: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
332: iload_1333: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
336: ldc #21 //String ,测试字符串数组长度
338: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
341: aload_2342: arraylength343: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
346: ldc #22 //String ,完成时间
348: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
351: new #4 //class java/util/Date
354: dup355: invokespecial #5 //Method java/util/Date."":()V
358: invokevirtual #13 //Method java/util/Date.getTime:()J
361: aload 4
363: invokevirtual #13 //Method java/util/Date.getTime:()J
366: lsub367: invokevirtual #14 //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
370: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
373: invokevirtual #16 //Method java/io/PrintStream.println:(Ljava/lang/String;)V
376: new #4 //class java/util/Date
379: dup380: invokespecial #5 //Method java/util/Date."":()V
383: astore 4
385: iconst_0386: istore 6
388: iload 6
390: iload_1391: if_icmpge 408
394: aload 5
396: aload 5
398: invokevirtual #24 //Method java/lang/String.concat:(Ljava/lang/String;)Ljava/lang/String;
401: astore_3402: iinc 6, 1
405: goto 388
408: getstatic #8 //Field java/lang/System.out:Ljava/io/PrintStream;
411: new #9 //class java/lang/StringBuilder
414: dup415: invokespecial #10 //Method java/lang/StringBuilder."":()V
418: ldc #25 //String String.concat 拼接测试,测试长度
420: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
423: iload_1424: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
427: ldc #21 //String ,测试字符串数组长度
429: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
432: aload_2433: arraylength434: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
437: ldc #22 //String ,完成时间
439: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
442: new #4 //class java/util/Date
445: dup446: invokespecial #5 //Method java/util/Date."":()V
449: invokevirtual #13 //Method java/util/Date.getTime:()J
452: aload 4
454: invokevirtual #13 //Method java/util/Date.getTime:()J
457: lsub458: invokevirtual #14 //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
461: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
464: invokevirtual #16 //Method java/io/PrintStream.println:(Ljava/lang/String;)V
467: new #4 //class java/util/Date
470: dup471: invokespecial #5 //Method java/util/Date."":()V
474: astore 4
476: iconst_0477: istore 7
479: iload 7
481: iload_1482: if_icmpge 533
485: ldc #3 //String
487: astore_3488: new #9 //class java/lang/StringBuilder
491: dup492: invokespecial #10 //Method java/lang/StringBuilder."":()V
495: astore 6
497: iconst_0498: istore 8
500: iload 8
502: aload_2503: arraylength504: if_icmpge 521
507: aload 6
509: aload 5
511: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
514: pop515: iinc 8, 1
518: goto 500
521: aload 6
523: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
526: astore_3527: iinc 7, 1
530: goto 479
533: getstatic #8 //Field java/lang/System.out:Ljava/io/PrintStream;
536: new #9 //class java/lang/StringBuilder
539: dup540: invokespecial #10 //Method java/lang/StringBuilder."":()V
543: ldc #26 //String StringBuilder 拼接测试,测试长度
545: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
548: iload_1549: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
552: ldc #21 //String ,测试字符串数组长度
554: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
557: aload_2558: arraylength559: invokevirtual #18 //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
562: ldc #22 //String ,完成时间
564: invokevirtual #12 //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
567: new #4 //class java/util/Date
570: dup571: invokespecial #5 //Method java/util/Date."":()V
574: invokevirtual #13 //Method java/util/Date.getTime:()J
577: aload 4
579: invokevirtual #13 //Method java/util/Date.getTime:()J
582: lsub583: invokevirtual #14 //Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
586: invokevirtual #15 //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
589: invokevirtual #16 //Method java/io/PrintStream.println:(Ljava/lang/String;)V
592: return}
String直接相加已经都被编译器优化成StringBuilder了,只是循环里优化的不太合理。所以,String相加快还是StringBuilder快,其实只是StringBuilder调用方式对比。。。
String.concat为什么快?
看看jdk1.8里这个方法的源代码就知道了:
publicString concat(String str) {int otherLen =str.length();if (otherLen == 0) {return this;
}int len =value.length;char buf[] = Arrays.copyOf(value, len +otherLen);
str.getChars(buf, len);return new String(buf, true);
}
简单粗暴,直接Arrays.copyOf,直接内存复制,这根StringBuilder原理类似,但是它不用初始化StringBuilder对象,只是每次concat都会创建一个新的String对象,所以在有些情况下它比StringBuilder要快一点。
结论:简单场景里,直接用+好了,反正编译器默认会优化成StringBuilder,毕竟对一般人来说加号可读性高一点。但是在循环中使用或者是比较复杂的应用场景里,还是尽量自己直接用StringBuilder或String concat。