1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        StringBuider 的效率一定高嗎?要看你怎么使用

        共 5511字,需瀏覽 12分鐘

         ·

        2020-08-16 13:15

        ??Java大聯(lián)盟

        ? 幫助萬(wàn)千Java學(xué)習(xí)者持續(xù)成長(zhǎng)

        關(guān)注



        作者|@后青春期的Keats

        www.cnblogs.com/keatsCoder/p/13212289.html


        B 站搜索:楠哥教你學(xué)Java

        獲取更多優(yōu)質(zhì)視頻教程



        ?引言

        都說(shuō) StringBuilder 在處理字符串拼接上效率要強(qiáng)于 String,但有時(shí)候我們的理解可能會(huì)存在一定的偏差。最近我在測(cè)試數(shù)據(jù)導(dǎo)入效率的時(shí)候就發(fā)現(xiàn)我以前對(duì) StringBuilder 的部分理解是錯(cuò)誤的。后來(lái)我通過(guò)實(shí)踐測(cè)試 + 找原理 的方式搞清楚了這塊的邏輯。現(xiàn)在將過(guò)程分享給大家


        測(cè)試用例

        我們的代碼在循環(huán)中拼接字符串一般有兩種情況

        • 第一種就是每次循環(huán)將對(duì)象中的幾個(gè)字段拼接成一個(gè)新字段,再賦值給對(duì)象

        • 第二種操作是在循環(huán)外創(chuàng)建一個(gè)字符串對(duì)象,每次循環(huán)向該字符串拼接新的內(nèi)容。循環(huán)結(jié)束后得到拼接好的字符串

        對(duì)于這兩種情況,我創(chuàng)建了兩個(gè)對(duì)照組

        第一組:

        在每次 For 循環(huán)中拼接字符串,即拼即用、用完即毀。分別使用 String 和 StringBuilder 拼接

            /**     * 循環(huán)內(nèi) String 拼接字符串,一次循環(huán)后銷(xiāo)毀     */    public static void useString(){        for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {            String str = str1 + i + str2 + i + str3 + i + str4 ;        }    }
        /** * 循環(huán)內(nèi) 使用 StringBuilder 拼接字符串,一次循環(huán)后銷(xiāo)毀 */ public static void useStringBuilder(){ for (int i = 0; i < CYCLE_NUM_BIGGER; i++) { StringBuilder sb = new StringBuilder(); String s = sb.append(str1).append(i).append(str2).append(i).append(str3).append(i).append(str4).toString(); } }

        第二組:

        多次 For 循環(huán)拼接一個(gè)字符串,循環(huán)結(jié)束后使用字符串,使用后由垃圾回收器回收。也是分別使用 String 和 StringBuilder 拼接

            /**     * 多次循環(huán)拼接成一個(gè)字符串 用 String     */    public static void useStringSpliceOneStr (){        String str = "";        for (int i = 0; i < CYCLE_NUM_LOWER; i++) {            str += str1 + str2 + str3 + str4 + i;        }    }
        /** * 多次循環(huán)拼接成一個(gè)字符串 用 StringBuilder */ public static void useStringBuilderSpliceOneStr(){ StringBuilder sb = new StringBuilder(); for (int i = 0; i < CYCLE_NUM_LOWER; i++) { sb.append(str1).append(str2).append(str3).append(str4).append(i); } }

        為了保證測(cè)試質(zhì)量,在每個(gè)測(cè)試項(xiàng)目進(jìn)行前。線程休息 2s,之后空跑 5 次熱身。最后執(zhí)行 5 次求平均時(shí)間的方式計(jì)算時(shí)間

           
         public static int executeSometime(int kind, int num) throws InterruptedException {        Thread.sleep(2000);        int sum = 0;        for (int i = 0; i < num + 5; i++) {            long begin = System.currentTimeMillis();
        switch (kind){ case 1: useString(); break; case 2: useStringBuilder(); break; case 3: useStringSpliceOneStr(); break; case 4: useStringBuilderSpliceOneStr(); break; default: return 0; }
        long end = System.currentTimeMillis();
        if(i > 5){ sum += (end - begin); } } return sum / num; }

        主方法

        public class StringTest {    public static final int CYCLE_NUM_BIGGER = 10_000_000;    public static final int CYCLE_NUM_LOWER = 10_000;    public static final String str1 = "張三";    public static final String str2 = "李四";    public static final String str3 = "王五";    public static final String str4 = "趙六";

        public static void main(String[] args) throws InterruptedException { int time = 0; int num = 5;
        time = executeSometime(1, num); System.out.println("String拼接 "+ CYCLE_NUM_BIGGER +" 次," + num + "次平均時(shí)間:" + time + " ms");
        time = executeSometime(2, num); System.out.println("StringBuilder拼接 "+ CYCLE_NUM_BIGGER +" 次," + num + "次平均時(shí)間:" + time + " ms");
        time = executeSometime(3, num); System.out.println("String拼接單個(gè)字符串 "+ CYCLE_NUM_LOWER +" 次," + num + "次平均時(shí)間:" + time + " ms");
        time = executeSometime(4, num); System.out.println("StringBuilder拼接單個(gè)字符串 "+ CYCLE_NUM_LOWER +" 次," + num + "次平均時(shí)間:" + time + " ms");
        }}


        測(cè)試結(jié)果

        測(cè)試結(jié)果如下

        結(jié)果分析

        第一組

        10_000_000 次循環(huán)拼接,在循環(huán)內(nèi)使用 String 和 StringBuilder 的效率是一樣的!為什么呢?

        使用 javap -c StringTest.class 反編譯查看兩個(gè)方法編譯后的文件:

        可以發(fā)現(xiàn) String 方法拼接字符串編譯器優(yōu)化后使用的就是 StringBuilder、因此用例1 和用例2 的效率是一樣的。

        第二組

        第二組的結(jié)果就是大家喜聞樂(lè)見(jiàn)的了,由于 10_000_000 次循環(huán)String 拼接實(shí)在太慢所以我采用了 10_000 次拼接來(lái)分析。

        分析用例3:雖然編譯器會(huì)對(duì) String 拼接做優(yōu)化,但是它每次在循環(huán)內(nèi)創(chuàng)建 StringBuilder 對(duì)象,在循環(huán)內(nèi)銷(xiāo)毀。下次循環(huán)他有創(chuàng)建。相比較用例4在循環(huán)外創(chuàng)建,多了 n 次 new 對(duì)象、銷(xiāo)毀對(duì)象的操作、n - 1 次將 StringBuilder 轉(zhuǎn)換成 String 的操作 。效率低也是理所應(yīng)當(dāng)了。

        擴(kuò)展

        第一組的測(cè)試還有一種寫(xiě)法:

            /**     * 循環(huán)內(nèi) 使用 StringBuilder 拼接字符串,一次循環(huán)后銷(xiāo)毀     */    public static void useStringBuilderOut(){        StringBuilder sb = new StringBuilder();        for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {//            sb.setLength(0);            sb.delete(0, sb.length());            String s = sb.append(str1).append(i).append(str2).append(i).append(str3).append(i).append(str4).toString();        }    }

        循環(huán)外創(chuàng)建 StringBuilder 每次循環(huán)開(kāi)始的時(shí)候清空 StringBuilder 的內(nèi)容然后拼接。這種寫(xiě)法無(wú)論使用 sb.setLength(0); 還是 sb.delete(0, sb.length()); 效率都比直接在循環(huán)內(nèi)使用 String / StringBuilder 慢。奈何才疏學(xué)淺我一直想不明白為什么他慢。我猜測(cè)是 new 對(duì)象的速度比重置長(zhǎng)度慢,于是這樣測(cè)試了以下:

            public static void createStringBuider() {        for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {            StringBuilder sb = new StringBuilder();        }    }
        public static void cleanStringBuider() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < CYCLE_NUM_BIGGER; i++) { sb.delete(0, sb.length()); } }

        但是結(jié)果是 cleanStringBuider 更快。


        結(jié)論

        編譯器會(huì)將 String 拼接優(yōu)化成使用 StringBuilder,但是還是有一些缺陷的。主要體現(xiàn)在循環(huán)內(nèi)使用字符串拼接,編譯器不會(huì)創(chuàng)建單個(gè) StringBuilder 以復(fù)用

        對(duì)于多次循環(huán)內(nèi)拼接一個(gè)字符串的需求:StringBuilder 很快,因?yàn)槠浔苊饬?n 次 new 對(duì)象、銷(xiāo)毀對(duì)象的操作,n - 1 次將 StringBuilder 轉(zhuǎn)換成 String 的操作

        StringBuilder 拼接不適用于循環(huán)內(nèi)每次拼接即用的操作方式。因?yàn)榫幾g器優(yōu)化后的 String 拼接也是使用 StringBuilder 兩者的效率一樣。后者寫(xiě)起來(lái)還方便...


        推薦閱讀

        1、Spring Boot+Vue項(xiàng)目實(shí)戰(zhàn)

        2、B站:4小時(shí)上手MyBatis Plus

        3、一文搞懂前后端分離

        4、快速上手Spring Boot+Vue前后端分離


        楠哥簡(jiǎn)介

        資深 Java 工程師,微信號(hào)?southwindss

        《Java零基礎(chǔ)實(shí)戰(zhàn)》一書(shū)作者

        騰訊課程官方 Java 面試官,今日頭條認(rèn)證大V

        GitChat認(rèn)證作者,B站認(rèn)證UP主(楠哥教你學(xué)Java)

        致力于幫助萬(wàn)千 Java 學(xué)習(xí)者持續(xù)成長(zhǎng)。




        有收獲,就點(diǎn)個(gè)在看?
        瀏覽 50
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品久久久久久久久久久不卡 | 少妇私密会所按摩到高潮呻吟 | 日韩无码一二三区 | 中文字幕av久久爽一区 | 激情摸胸视频 | 午夜伦情电午夜伦情电影如如视频 | 久久久9999 | 欧美色影视 | 香蕉国产片 | 91豆花在线观看 |