看到网上有人已经做过对比,并且贴出了代码,然后我运行了之后发现跟我分析的结论差距很大。发现他的代码有个问题,UUID.randomUUID() 首次调用耗时会很高,这个耗时被计算给了String,这对String是不公平的。

原始代码参见:http://www.codes51.com/article/detail_99554.html

修改后的测试代码如下:

import java.util.Date;
import java.util.UUID; public class StringTest { public static void main(String[] args) {
int testLength = 10000;
String[] arr = new String[2];
String str = ""; Date start = new Date();
String testStr = UUID.randomUUID().toString();
System.out.println("首次生成randomUUID耗时:" + (new Date().getTime() - start.getTime())); start = new Date();
for (int i = 0; i < testLength; i++) {
testStr = UUID.randomUUID().toString();
}
System.out.println("非首次生成randomUUID " + testLength + "次耗时:" + (new Date().getTime() - start.getTime())); start = new Date();
for (int i = 0; i < testLength; i++) {
str = testStr + testStr;
}
System.out.println("String 拼接测试,测试长度" + testLength + ",测试字符串数组长度" + arr.length + ",完成时间" + (new Date().getTime() - start.getTime())); start = new Date();
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 = new Date();
StringBuilder sb;
for (int i = 0; i < testLength; i++) {
str = "";
sb = new StringBuilder();
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 class StringTest {
public StringTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
0: sipush 10000
3: istore_1
4: iconst_2
5: anewarray #2 // class java/lang/String
8: astore_2
9: ldc #3 // String
11: astore_3
12: new #4 // class java/util/Date
15: dup
16: invokespecial #5 // Method java/util/Date."<init>":()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: dup
36: invokespecial #10 // Method java/lang/StringBuilder."<init>":()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: dup
48: invokespecial #5 // Method java/util/Date."<init>":()V
51: invokevirtual #13 // Method java/util/Date.getTime:()J
54: aload 4
56: invokevirtual #13 // Method java/util/Date.getTime:()J
59: lsub
60: 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: dup
73: invokespecial #5 // Method java/util/Date."<init>":()V
76: astore 4
78: iconst_0
79: istore 6
81: iload 6
83: iload_1
84: 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: dup
108: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
111: ldc #17 // String 非首次生成randomUUID
113: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
116: iload_1
117: 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: dup
129: invokespecial #5 // Method java/util/Date."<init>":()V
132: invokevirtual #13 // Method java/util/Date.getTime:()J
135: aload 4
137: invokevirtual #13 // Method java/util/Date.getTime:()J
140: lsub
141: 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: dup
154: invokespecial #5 // Method java/util/Date."<init>":()V
157: astore 4
159: iconst_0
160: istore 6
162: iload 6
164: iload_1
165: if_icmpge 195
168: new #9 // class java/lang/StringBuilder
171: dup
172: invokespecial #10 // Method java/lang/StringBuilder."<init>":()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_3
189: 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: dup
202: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
205: ldc #20 // String String 拼接测试,测试长度
207: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
210: iload_1
211: 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_2
220: arraylength
221: 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: dup
233: invokespecial #5 // Method java/util/Date."<init>":()V
236: invokevirtual #13 // Method java/util/Date.getTime:()J
239: aload 4
241: invokevirtual #13 // Method java/util/Date.getTime:()J
244: lsub
245: 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: dup
258: invokespecial #5 // Method java/util/Date."<init>":()V
261: astore 4
263: iconst_0
264: istore 6
266: iload 6
268: iload_1
269: if_icmpge 317
272: ldc #3 // String
274: astore_3
275: iconst_0
276: istore 7
278: iload 7
280: aload_2
281: arraylength
282: if_icmpge 311
285: new #9 // class java/lang/StringBuilder
288: dup
289: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
292: aload_3
293: 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_3
305: 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: dup
324: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
327: ldc #23 // String String 使用循环 拼接测试,测试长度
329: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
332: iload_1
333: 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_2
342: arraylength
343: 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: dup
355: invokespecial #5 // Method java/util/Date."<init>":()V
358: invokevirtual #13 // Method java/util/Date.getTime:()J
361: aload 4
363: invokevirtual #13 // Method java/util/Date.getTime:()J
366: lsub
367: 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: dup
380: invokespecial #5 // Method java/util/Date."<init>":()V
383: astore 4
385: iconst_0
386: istore 6
388: iload 6
390: iload_1
391: 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_3
402: 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: dup
415: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
418: ldc #25 // String String.concat 拼接测试,测试长度
420: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
423: iload_1
424: 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_2
433: arraylength
434: 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: dup
446: invokespecial #5 // Method java/util/Date."<init>":()V
449: invokevirtual #13 // Method java/util/Date.getTime:()J
452: aload 4
454: invokevirtual #13 // Method java/util/Date.getTime:()J
457: lsub
458: 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: dup
471: invokespecial #5 // Method java/util/Date."<init>":()V
474: astore 4
476: iconst_0
477: istore 7
479: iload 7
481: iload_1
482: if_icmpge 533
485: ldc #3 // String
487: astore_3
488: new #9 // class java/lang/StringBuilder
491: dup
492: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
495: astore 6
497: iconst_0
498: istore 8
500: iload 8
502: aload_2
503: arraylength
504: if_icmpge 521
507: aload 6
509: aload 5
511: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
514: pop
515: iinc 8, 1
518: goto 500
521: aload 6
523: invokevirtual #15 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
526: astore_3
527: 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: dup
540: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
543: ldc #26 // String StringBuilder 拼接测试,测试长度
545: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
548: iload_1
549: 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_2
558: arraylength
559: 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: dup
571: invokespecial #5 // Method java/util/Date."<init>":()V
574: invokevirtual #13 // Method java/util/Date.getTime:()J
577: aload 4
579: invokevirtual #13 // Method java/util/Date.getTime:()J
582: lsub
583: 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里这个方法的源代码就知道了:

public String 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。

java String、String.concat和StringBuilder性能对比的更多相关文章

  1. Java中String、StringBuffer、StringBuilder区别与理解

    一.先比较String.StringBuffer.StringBuilder变量的HashCode值 使用System.out.println(obj.hashcode())输出的时对象的哈希码, 而 ...

  2. java中String StringBuilder StringBuffer比较和效率(性能)测试

    string stringbuilder stringbuffer三者的区别 从JDK源码看,String.StringBuilder.StringBuffer都是存放在char[] 数组字符串. 简 ...

  3. 不同Framework下StringBuilder和String的性能对比,及不同Framework性能比(附Demo)

    本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 文章是哥(mephisto)写的,SourceLink 阅读目录 介绍 环境搭建 测试用例 MSDN说明 ...

  4. StringBuilder和string.Format性能对比

    本文由博主(YinaPan)原创,转载请注明出处:http://www.cnblogs.com/YinaPan/p/sbformat.html StringBuilder的性能优于string.For ...

  5. 【Java必修课】判断String是否包含子串的四种方法及性能对比

    1 简介 判断一个字符串是否包含某个特定子串是常见的场景,比如判断一篇文章是否包含敏感词汇.判断日志是否有ERROR信息等.本文将介绍四种方法并进行性能测试. 2 四种方法 2.1 JDK原生方法St ...

  6. Java中的常用类:包装类、String、StringBuffer、StringBuilder、Math、System、Arrays、BigInteger、BigDecimal、Data、Calendar

    一.包装类 √ 二.String类 ★ 三.StringBuffer和StringBuilder类 ★ 四.Math类 五.System类 六.Arrays类 七.BigInteger类和BigDec ...

  7. 探秘Java中String、StringBuilder以及StringBuffer

    探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问 到的地方,今天就来和大家一起学习 ...

  8. java中String类、StringBuilder类和StringBuffer类详解

    本位转载自http://www.cnblogs.com/dolphin0520/p/3778589.html  版权声明如下: 作者:海子 出处:http://www.cnblogs.com/dolp ...

  9. Java中String、StringBuilder以及StringBuffer

    原文出处: 海子 相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String.StringBuilder和StringBuffe ...

随机推荐

  1. 7.27 NOIP模拟测试9 随 (rand)+单(single)+题(problem)

    T1 随 (rand) dp+矩阵优化+原根 看着题解懵了一晚上加一上午,最后还是看了DeepinC的博客才把暴力码出来,正解看得一知半解,循环矩阵也不太明白,先留坑吧.暴力里用二维矩阵快速幂会tle ...

  2. [LeetCode] 367. Valid Perfect Square 检验完全平方数

    Given a positive integer num, write a function which returns True if num is a perfect square else Fa ...

  3. CSP-S考前救急(考试前还是别复习了,事实证明复习了也没考到...

    “不要为明天而忧虑,因为明天自有明天的忧虑:一天的难处一天当就够了.” 念念不忘,必有回响. 考试结束前15分钟停止写代码.然后按照以下顺序进行检查: -检查文件名是否写错-检查是否打开文件输入输出 ...

  4. OsharpNS轻量级.net core快速开发框架简明入门教程-多上下文配置(多个数据库的使用)

    OsharpNS轻量级.net core快速开发框架简明入门教程 教程目录 从零开始启动Osharp 1.1. 使用OsharpNS项目模板创建项目 1.2. 配置数据库连接串并启动项目 1.3. O ...

  5. 路径规划基础A*算法

    1,Dijkstra’s  算法 一种发散性寻找最短路径算法. 由起点开始向四周开始发散,直到碰到目标点为止.这时就是最短路径.优点:能找到与目标点的最短路径:缺点:搜索花费的时间会比较长. 2,Gr ...

  6. Java8 新特性 Optional 类

    Optional 类的简介   Optional类的是来自谷歌Guava的启发,然后就加入到Java8新特性中去了.Optional类主要就是为子决解价值亿万的错误,空指针异常.   Optional ...

  7. jQuery学习路线&review

    学习途径:http://www.w3school.com.cn/jquery/index.asp 路线图 转载自:https://www.cnblogs.com/lanren2017/p/723720 ...

  8. ISO C语言新标准(C11)

    新特性[2]有些和C++11是对应的,如线程和UTF-8: 对齐处理(Alignment)的标准化(包括_Alignas标志符,alignof运算符, aligned_alloc函数以及<std ...

  9. 【02】Saltstack:Grains and Pillar

    写在前面的话 上一节谈及了 Saltstack 的安装和初始化配置,本节将谈谈 Saltstack 中两个重要的东西,Grains 和 Pillar. 数据系统 Grains 入门 Grains 是静 ...

  10. EF Core中如何设置数据库表自己与自己的多对多关系

    本文的代码基于.NET Core 3.0和EF Core 3.0 有时候在数据库设计中,一个表自己会和自己是多对多关系. 在SQL Server数据库中,现在我们有Person表,代表一个人,建表语句 ...