zoukankan      html  css  js  c++  java
  • java String、String.concat和StringBuilder性能对比

    看到网上有人已经做过对比,并且贴出了代码,然后我运行了之后发现跟我分析的结论差距很大。发现他的代码有个问题,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。

  • 相关阅读:
    mysql不停库不锁表在线主从配置
    MySQL5.7不停机不锁表主从同步实战
    另类 k8s 集群灾难恢复方法:用 master 服务器镜像恢复出新集群
    我在新西兰卖挖掘机之续篇十八------坚持的秘诀(坚持的秘诀就2条,第一是初始摩擦力要足够小(开头不能太困难,时间不能太久);第二是坚持的事情能够不断地给你提供某种真实的好处)(其实就是正反馈,加上一点点兴趣)
    分布式系统中那些不靠谱任务-CAP 到底是什么
    分布式系统架构中关于切分数据库事务的 ACID 性质的可用性与性能-BASE 理论和CAP 定理
    SSO协议-SAML和OAuth2
    领域建模
    基于 Docker 容器Net Core微服务部署 Nginx 集群架构
    消息队列和分布式锁
  • 原文地址:https://www.cnblogs.com/oceanking/p/5601273.html
Copyright © 2011-2022 走看看