for循環中為什么最好使用StringBuilder


for循環中為什么最好使用StringBuilder?

此次操作都是在以下環境中

環境 版本
archlinux 4.16.13-1-ARCH
jdk java version "1.8.0_172"

先看不使用StringBuilder的情況

public class Test {

    public static void testForStr(){
        String str = "";
        for (int i=0; i<10; i++){
            str += i;
        }
    }

    public static void main(String[] args){
        Test.testForStr();
    }
}

為了更好的分析編譯器到底干了什么,我們需要使用javap命令進行字節碼分析,終端執行以下命令:

javac Test.java
javap -c Test.class

屏幕輸出的結果為:

Compiled from "Test.java"
public class top.freesh.laughably.Test {
  public top.freesh.laughably.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void testForStr();
    Code:
			 //從常量池引用#2並推向棧頂
       0: ldc           #2                  // String abc
			 //將棧頂的引用存入第一個局部變量
       2: astore_0
			 //將int型常量0推入棧頂
       3: iconst_0
			 //將棧頂0存入第二個int型變量中
       4: istore_1
			 //第二個int局部變量進棧
       5: iload_1
			 //將byte型常量10推入棧頂
       6: bipush        10
			 //如果棧頂兩個值大於等於0(此時0-10)則跳轉36(code)
       8: if_icmpge     36
			 //創建StringBuilder對象,其引用進棧
      11: new           #3                  // class java/lang/StringBuilder
			//復制棧頂引用並進棧(此時棧頂有兩個對象的引用)
      14: dup
			//調用StringBuilder的構造方法(消耗掉一個對象引用)
      15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
			//第一個引用變量入棧(第一次為:abc)
      18: aload_0
			//調用append方法,消耗一個對象引用和一個abc,並返回一個對象引用存入棧頂(注意到append()方法返回的是StringBuilder引用)
      19: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
			//第二個int型變量入棧(0入棧,此處對應循環體中的i)
      22: iload_1
			//調用append方法,消耗一個對象引用和o,並返回一個對象引用存入棧頂(同19)
      23: invokevirtual #6                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
			//調用toString方法,並將產生的String存入棧頂
      26: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
			//將棧頂String存入本地第一個引用變量中
      29: astore_0
			//將本地第二個int型變量增加1
      30: iinc          1, 1
			//無條件跳轉到5(code)
      33: goto          5
			//返回
      36: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #8                  // Method testForStr:()V
       3: return
}

可見for循環中,對字符串進行加法操作,都會產生一個StringBuilder對象,這雖然減少了jvm常量池的壓力,但也無疑增加了jvm中新生代的壓力(增加了垃圾回收的幾率)

再來看看使用StringBuilder的情況吧

public class Test {

    public static void testForStr(){
		//和上邊的代碼邏輯一樣,只是這里使用StringBuilder連接字符串
        StringBuilder stringBuilder = new StringBuilder("abc");
        for (int i=0; i<127; i++){
            stringBuilder.append(i);
        }
    }

    public static void main(String[] args){
        Test.testForStr();
    }
}

使用同樣的命令分析jvm指令

javac Test.java
javap -c Test.class
Compiled from "Test.java"
public class top.freesh.laughably.Test {
  public top.freesh.laughably.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void testForStr();
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: ldc           #3                  // String abc
       6: invokespecial #4                  // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
			 //存儲的是StringBuilder引用
       9: astore_0
      10: iconst_0
      11: istore_1
      12: iload_1
      13: bipush        10
      15: if_icmpge     30
      18: aload_0
      19: iload_1
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
			//此處將append產生的對象引用(此時位於棧頂)出棧,因為aload_0每次都會將引用入棧
      23: pop
      24: iinc          1, 1
      27: goto          12
      30: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #6                  // Method testForStr:()V
       3: return
}

完全沒有問題,每次循環也不會產生StringBuilder對象了

  • 建議大家以后在循環中使用StringBuilder來操作字符串拼接工作

  • 普通代碼中使用加號連接字符串會被jvm優化成使用StringBuilder拼接(感興趣的朋友可以試一下)

本博客為博主原創,轉載請注明出處


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM