之前在閱讀《阿里巴巴Java開(kāi)發(fā)手冊(cè)》時(shí),發(fā)現(xiàn)有一條是關(guān)于循環(huán)體中字符串拼接的建議,具體內(nèi)容如下:



          那么我們首先來(lái)用例子來(lái)看看在循環(huán)體中用 + 或者用 StringBuilder 進(jìn)行字符串拼接的效率如何吧(JDK版本為 jdk1.8.0_201)。
          package com.wupx.demo; /** * @author wupx * @date 2019/10/23 */ public class
          StringConcatDemo { public static void main(String[] args) { long s1 =
          System.currentTimeMillis(); new StringConcatDemo().addMethod();
          System.out.println("使用 + 拼接:" + (System.currentTimeMillis() - s1)); s1 =
          System.currentTimeMillis(); new StringConcatDemo().stringBuilderMethod();
          System.out.println("使用 StringBuilder 拼接:" + (System.currentTimeMillis() - s1));
          } public String addMethod() { String result = ""; for (int i = 0; i < 100000;
          i++) { result += (i + "武培軒"); } return result; } public String
          stringBuilderMethod() { StringBuilder result = new StringBuilder(); for (int i
          = 0; i < 100000; i++) { result.append(i).append("武培軒"); } return
          result.toString(); } }
          執(zhí)行結(jié)果如下:
          使用 + 拼接:29282 使用 StringBuilder 拼接:4
          為什么這兩種方法的時(shí)間會(huì)差這么多呢?接下來(lái)讓我們一起進(jìn)一步研究。

          為什么 StringBuilder 比 + 快這么多?

          從字節(jié)碼層面來(lái)看下,為什么循環(huán)體中字符串拼接 StringBuilder 比 + 快這么多?

          使用 javac StringConcatDemo.java 命令編譯源文件,使用 javap -c StringConcatDemo
          命令查看字節(jié)碼文件的內(nèi)容。

          其中 addMethod() 方法的字節(jié)碼如下:
          public java.lang.String addMethod(); Code: 0: ldc #16 // String 2: astore_1
          3: iconst_0 4: istore_2 5: iload_2 6: ldc #17 // int 100000 8: if_icmpge 41 11:
          new #7 // class java/lang/StringBuilder 14: dup 15: invokespecial #8 // Method
          java/lang/StringBuilder."<init>":()V 18: aload_1 19: invokevirtual #10 //
          Method
          java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          22: iload_2 23: invokevirtual #18 // Method
          java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 26: ldc #19 //
          String wupx 28: invokevirtual #10 // Method
          java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          31: invokevirtual #12 // Method
          java/lang/StringBuilder.toString:()Ljava/lang/String; 34: astore_1 35: iinc 2,
          1 38: goto 5 41: aload_1 42: areturn
          可以看出,第 8 行到第 38 行構(gòu)成了一個(gè)循環(huán)體:在第 8 行的時(shí)候做條件判斷,如果不滿足循環(huán)條件,則跳轉(zhuǎn)到 41 行。編譯器做了一定程度的優(yōu)化,在 11
          行 new 了一個(gè) StringBuilder 對(duì)象,然后再 19 行、23 行、28 行進(jìn)行了三次 append() 方法的調(diào)用,不過(guò)每次循環(huán)都會(huì)重新
          new 一個(gè) StringBuilder 對(duì)象。

          再來(lái)看 stringBuilderMethod() 方法的字節(jié)碼:
          public java.lang.String stringBuilderMethod(); Code: 0: new #7 // class
          java/lang/StringBuilder 3: dup 4: invokespecial #8 // Method
          java/lang/StringBuilder."<init>":()V 7: astore_1 8: iconst_0 9: istore_2 10:
          iload_2 11: ldc #17 // int 100000 13: if_icmpge 33 16: aload_1 17: iload_2 18:
          invokevirtual #18 // Method
          java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 21: ldc #19 //
          String wupx 23: invokevirtual #10 // Method
          java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          26: pop 27: iinc 2, 1 30: goto 10 33: aload_1 34: invokevirtual #12 // Method
          java/lang/StringBuilder.toString:()Ljava/lang/String; 37: areturn
          13 行到 30 行構(gòu)成了循環(huán)體,可以看出,在第4行(循環(huán)體外)就構(gòu)建好了 StringBuilder 對(duì)象,然后再循環(huán)體內(nèi)只進(jìn)行 append()
          方法的調(diào)用。

          由此可以看出,在 for 循環(huán)中,使用 + 進(jìn)行字符串拼接,每次都是 new 了一個(gè) StringBuilder,然后再把 String 轉(zhuǎn)成
          StringBuilder,再進(jìn)行 append,而頻繁的新建對(duì)象不僅要耗費(fèi)很多時(shí)間,還會(huì)造成內(nèi)存資源的浪費(fèi)。這就從字節(jié)碼層面解釋了為什么不建議在循環(huán)體內(nèi)使用
          + 去進(jìn)行字符串的拼接。

          接下來(lái)再來(lái)讓我們看下使用 + 或者 StringBuilder 拼接字符串的原理吧。

          使用 + 拼接字符串

          在 Java 開(kāi)發(fā)中,最簡(jiǎn)單常用的字符串拼接方法就是直接使用 + 來(lái)完成:
          String boy = "wupx"; String girl = "huyx"; String love = boy + girl;
          反編譯后的內(nèi)容如下:(使用的反編譯工具為 jad)
          String boy = "wupx"; String girl = "huyx"; String love = (new
          StringBuilder()).append(boy).append(girl).toString();
          通過(guò)查看反編譯以后的代碼,可以發(fā)現(xiàn),在字符串常量在拼接過(guò)程中,是將 String 轉(zhuǎn)成了 StringBuilder 后,使用其 append()
          方法進(jìn)行處理的。

          那么也就是說(shuō),Java中的 + 對(duì)字符串的拼接,其實(shí)現(xiàn)原理是使用 StringBuilder 的 append() 來(lái)實(shí)現(xiàn)的,使用 + 拼接字符串,其實(shí)只是
          Java 提供的一個(gè)語(yǔ)法糖。

          使用 StringBuilder 拼接字符串

          StringBuilder 的 append 方法就是第二個(gè)常用的字符串拼接姿勢(shì)了。

          和 String 類類似,StringBuilder 類也封裝了一個(gè)字符數(shù)組,定義如下:
          char[] value;
          與 String 不同的是,它并不是 final 的,所以是可以修改的。另外,與 String
          不同,字符數(shù)組中不一定所有位置都已經(jīng)被使用,它有一個(gè)實(shí)例變量,表示數(shù)組中已經(jīng)使用的字符個(gè)數(shù),定義如下:
          int count;
          其 append() 方法源碼如下:
          public StringBuilder append(String str) { super.append(str); return this; }
          該類繼承了 AbstractStringBuilder 類,看下其 append() 方法:
          public AbstractStringBuilder append(String str) { if (str == null) return
          appendNull(); int len = str.length(); ensureCapacityInternal(count + len);
          str.getChars(0, len, value, count); count += len; return this; }
          首先判斷拼接的字符串 str 是不是 null,如果是,調(diào)用 appendNull() 方法進(jìn)行處理,appendNull() 方法的源碼如下:
          private AbstractStringBuilder appendNull() { int c = count;
          ensureCapacityInternal(c + 4); final char[] value = this.value; value[c++] =
          'n'; value[c++] = 'u'; value[c++] = 'l'; value[c++] = 'l'; count = c; return
          this; }
          如果字符串 str 不為 null,則判斷拼接后的字符數(shù)組長(zhǎng)度是否超過(guò)當(dāng)前數(shù)組長(zhǎng)度,如果超過(guò),則調(diào)用 Arrays.copyOf()
          方法進(jìn)行擴(kuò)容并復(fù)制,ensureCapacityInternal() 方法的源碼如下:
          private void ensureCapacityInternal(int minimumCapacity) { if (minimumCapacity
          - value.length > 0) { value = Arrays.copyOf(value,
          newCapacity(minimumCapacity)); } }
          最后,將拼接的字符串 str 復(fù)制到目標(biāo)數(shù)組 value 中。
          str.getChars(0, len, value, count);
          總結(jié)

          本文針對(duì)《阿里巴巴Java開(kāi)發(fā)手冊(cè)》中的循環(huán)體中拼接字符串建議出發(fā),從字節(jié)碼層面,來(lái)解釋為什么 StringBuilder 比 +
          快,還分別介紹了字符串拼接中 + 和 StringBuilder 的原理,因此在循環(huán)體拼接字符串時(shí),應(yīng)該使用 StringBuilder 的 append()
          去完成拼接。


          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問(wèn)題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                挨操必网站 | 伊人精品视频在线观看 | 日韩激情小说 | 男女在床干事免费视频 | 秋霞午夜电影午夜伦片 |