Java String 字串拼接的三種方式與效率對比分析
String 字串的拼接
+ 號拼接
通過 +
號拼接是最常見的拼接方式了。
String jeremy = "Jeremy";
String tsai = "Tsai";
String jeremytsai = jeremy + tsai;
觀察位元組碼
L0
LINENUMBER 12 L0
LDC "Jeremy"
ASTORE 1
L1
LINENUMBER 13 L1
LDC "Tsai"
ASTORE 2
L2
LINENUMBER 14 L2
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 3
我們不難發現, String str = a + b
被JDK編譯器在編譯位元組碼的時候幫我們優化成為了以下語句:
String jeremytsai = new StringBuilder().append(jeremy).append(tsai);
區別與C++的運算子過載,這僅僅是JDK內部優化的語法糖,Java本身沒有運算子過載之說。但是要注意這種語法糖, 只對於同一行才有效,例如:
s = hello + world + jeremy + tsai;
注: 他們都是變數。
若將其拆分
s = hello;
s += world;
s += jeremy;
s += tsai;
檢視位元組碼:
L5
LINENUMBER 15 L5
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 5
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 5
L6
LINENUMBER 16 L6
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 5
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 3
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 5
L7
LINENUMBER 17 L7
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 5
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 4
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 5
可以看到,每次拼接都會建立一個StringBuilder。所以說這只是語法糖,不能算符號過載,稍加不注意就行影響效率,因為我們日常編寫不可能用+
號拼接很長的串,中間還有涉及業務邏輯。
String.concat(String str) 方法
concat方法是String給我們提供的拼接字串的方法。
String jeremy = "Jeremy";
String tsai = "Tsai";
String jeremytsai = jeremy.concat(tsai);
原始碼描述如下:
將指定的字串連線到該字串的末尾。
如果引數字串的長度為0,則返回此String物件。
否則,返回一個String物件,該物件表示一個字元序列,該字元序列是此String物件表示的字元序列與引數字串表示的字元序列的串聯。
原始碼
public String concat(String str) {
int otherLen = str.length(); // 獲取引數字串長度
if (otherLen == 0) {
return this; // 引數長度為0,返回自身
}
int len = value.length; //獲取自身長度
char buf[] = Arrays.copyOf(value, len + otherLen); // 得到一個包含當前字元序列,長度為 // 兩者之和的字元陣列
str.getChars(buf, len); // 從當前字元序列長度開始,將引數的字元序列寫入buf字元陣列
return new String(buf, true); // 建立新的String物件並返回。
}
跟描述一樣。
StringBuilder 拼接字串
String jeremy = "jeremy";
String tsai = "tsai";
String jeremytsai = new StringBuilder().append(jeremy).append(tsai).toString();
檢視位元組碼
L0
LINENUMBER 13 L0
LDC "jeremy"
ASTORE 1
L1
LINENUMBER 14 L1
LDC "tsai"
ASTORE 2
L2
LINENUMBER 15 L2
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ALOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 3
可以看出,與 +
號拼接一致。
String 字串拼接效率對比
先知道一點,String在Java中是不可變物件,因此每次拼接都是生成新的String物件,為了解決頻繁的記憶體開闢消耗資源,才有了StringBuilder
類。在+
拼接過程中,JDK預設優化成為StringBuilder以提高執行效率。
但是這裡又出現了一個問題,當且僅有兩個字串拼接生成一個新的字串,這個預設優化的優勢就體現不出來了。因為本來只需要三份String空間,預設優化StringBuilder的情況下,還需要一份StringBuilder的空間,多開闢了一份空間,肯定會對效能有所影響,為了驗證這一猜想,簡單寫了一個小程式測試.
// 定義要拼接的陣列
String jeremy = "Jeremy";
String tsai = "Tsai";
// 然後分別記錄各個拼接的耗時
String jeremytsai1 = jeremy + tsai;
String jeremytsai2 = jeremy.concat(tsai);
結果如我所料:
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:100納秒 concat拼接 jeremytsai 的執行時間為:200納秒
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:200納秒 concat拼接 jeremytsai 的執行時間為:100納秒
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:100納秒 concat拼接 jeremytsai 的執行時間為:200納秒
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:100納秒 concat拼接 jeremytsai 的執行時間為:200納秒
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:200納秒 concat拼接 jeremytsai 的執行時間為:100納秒
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:0納秒 concat拼接 jeremytsai 的執行時間為:100納秒
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:100納秒 concat拼接 jeremytsai 的執行時間為:300納秒
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:100納秒 concat拼接 jeremytsai 的執行時間為:100納秒
-------------------------------------------
+/StringBuilder拼接 jeremytsai 的執行時間為:100納秒 concat拼接 jeremytsai 的執行時間為:0納秒
在100000次拼接JeremyTsai中,+/StringBuilder快的次數為: 29510,concat快的次數為:70490
迴圈拼接
迴圈拼接是一種特殊的拼接,其形式一般為:
String 結果;
for(迴圈條件) {
String 中間量;
// 計算
結果 += 中間量
}
在這種情況下,JDk的預設優化就顯得很笨拙了,例如:
String jeremytsai = "";
for (int i = 0; i < 100; i++) {
jeremytsai += "JeremyTsai\n";
}
檢視原始碼
L0
LINENUMBER 15 L0
LDC ""
ASTORE 1
L1
LINENUMBER 16 L1
ICONST_0
ISTORE 2
L2
FRAME APPEND [java/lang/String I]
ILOAD 2
BIPUSH 100
IF_ICMPGE L3
L4
LINENUMBER 17 L4
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
LDC "JeremyTsai\n"
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 1
L5
LINENUMBER 16 L5
IINC 2 1
GOTO L2
可以看出,+
號的預設優化使得每個迴圈體內部都要new一個新的StringBuilder進行拼接,這會大大降低效能。同理,concat也一樣,每次拼接會生成新的String物件,會頻繁開闢空間,效率不高。
故,在迴圈體中的字串拼接推薦使用StringBuilder
StringBuilder jeremytsai = new StringBuilder();
for (int i = 0; i < 100; i++) {
jeremytsai.append("jeremy").append("tsai\n");
}
字串拼接總結
在非迴圈體中的字串拼接,若只是兩個字串拼接,推薦使用concat
。
多字元或迴圈體中拼接字串優先使用StringBuilder
,提高效率,還能鏈式程式設計。不要過於依賴+
號拼接的語法糖,但是簡單拼接還是推薦使用的。畢竟能省很多程式碼量。
相關文章
- Java 中拼接 String 的 N 種方式Java
- Java中常見字串拼接九種方式Java字串
- Java程式碼中字串拼接方式分析Java字串
- ECode1024 | String拼接方法concat與+效率比較問題
- 轉換String三種方式比較:toString()、String.valueOf()、(String)
- 詳解Python拼接字串的七種方式Python字串
- String/StringBuilder字串拼接操作UI字串
- Java 字串比較、拼接問題Java字串
- Java與眾不同的字串-String類Java字串
- pl/sql中三種遊標迴圈效率對比SQL
- 字串拼接運算比較字串
- Java中的字串操作(比較String,StringBuiler和StringBuffer)Java字串UI
- Java頻繁的進行字串拼接不要用 String! StringBuffer是最佳選擇!Java字串
- Java字串建立方式比較Java字串
- Python 中字串拼接的 N 種方法Python字串
- js array陣列拼接 push() concat() 的方法效率對比,差10倍JS陣列
- java排序方式對比Java排序
- List集合去重方式及效率對比
- C#例項化物件的三種方式及效能對比C#物件
- 幀動畫的多種實現方式與效能對比動畫
- python3 拼接字串的7種方法Python字串
- 5種常見的Python拼接字串方法!Python字串
- Java-string字串Java字串
- 12月27日雲棲精選夜讀|Python拼接字串的七種方式Python字串
- 用Jupyter+pandas資料分析,6種資料格式效率對比
- Go語言字串高效拼接(三)Go字串
- python中7種方法實現字串的拼接Python字串
- Java 反射機制的三種方式Java反射
- Java字串拼接寫法 joiner.onJava字串
- 陣列去重的各種方式對比陣列
- dotnet 6 使用 string.Create 提升字串建立和拼接效能字串
- 使用String. localeCompare比較字串字串
- java基礎:String — 字串常量池與intern(二)Java字串
- Java 8中字串拼接新姿勢:StringJoinerJava字串
- 在 .NET 中建立物件的幾種方式的對比物件
- Kotlin 與 Java 對比KotlinJava
- Java陣列轉列表方式對比Java陣列
- js實現繼承的幾種方式和對比JS繼承