字串連線運算子()
+
符號可以表示 Java 中的三個不同運算子:
- 如果
+
之前沒有運算元,則它是一元的 Plus 運算子。 - 如果有兩個運算元,它們都是數字。那麼它是二元加法運算子。
- 如果有兩個運算元,並且其中至少有一個是
String
,那麼它就是二元連線運算子。
在簡單的情況下,Concatenation 運算子連線兩個字串以提供第三個字串。例如:
String s1 = "a String";
String s2 = "This is " + s1; // s2 contains "This is a String"
當兩個運算元中的一個不是字串時,它將轉換為 String
,如下所示:
-
一個運算元,其型別是基本型別被轉換為如果通過呼叫
toString()
在裝箱值。 -
通過呼叫運算元的
toString()
方法轉換其型別為引用型別的運算元。如果運算元是null
,或者如果toString()
方法返回null
,則使用字串文字null
。
例如:
int one = 1;
String s3 = "One is " + one; // s3 contains "One is 1"
String s4 = null + " is null"; // s4 contains "null is null"
String s5 = "{1} is " + new int[]{1}; // s5 contains something like
// "{} is [I@xxxxxxxx"
s5
示例的解釋是陣列型別的 toString()
方法繼承自 java.lang.Object
,行為是生成一個由型別名稱和物件的標識雜湊碼組成的字串。
指定 Concatenation 運算子以建立新的 String
物件,除非表示式是常量表示式。在後一種情況下,表示式在編譯型別時計算,其執行時值等同於字串文字。這意味著在拆分長字串文字時沒有執行時開銷,如下所示:
String typing = "The quick brown fox " +
"jumped over the " +
"lazy dog"; // constant expression
優化和效率
如上所述,除常量表示式外,每個字串連線表示式都會建立一個新的 String
物件。考慮以下程式碼:
public String stars(int count) {
String res = "";
for (int i = 0; i < count; i++) {
res = res + "*";
}
return res;
}
在上面的方法中,迴圈的每次迭代將建立一個比前一次迭代長一個字元的新 String
。每個連線都複製運算元字串中的所有字元以形成新的 String
。因此,stars(N)
將:
- 創造
N
新的String
物件,扔掉除了最後一個之外的所有物件, - 複製
N * (N + 1) / 2
個字元,和 - 生成
O(N^2)
位元組的垃圾。
這對於大型的 tihuan 來說非常昂貴 23。實際上,任何在迴圈中連線字串的程式碼都容易出現這個問題。寫這個的更好方法如下:
public String stars(int count) {
// Create a string builder with capacity 'count'
StringBuilder sb = new StringBuilder(count);
for (int i = 0; i < count; i++) {
sb.append("*");
}
return sb.toString();
}
理想情況下,你應該設定 StringBuilder
的容量,但如果這不可行,則類將自動增大構建器用於儲存字元的後備陣列。 (注意:實現會以指數方式擴充套件後備陣列。這種策略可以將字元數量複製到 O(N)
而不是 O(N^2)
。)
有些人將此模式應用於所有字串連線。但是,這是不必要的,因為 JLS 允許 Java 編譯器優化單個表示式中的字串連線。例如:
String s1 = ...;
String s2 = ...;
String test = "Hello " + s1 + ". Welcome to " + s2 + "\n";
將典型的位元組碼編譯器是這樣進行優化;
StringBuilder tmp = new StringBuilder();
tmp.append("Hello ")
tmp.append(s1 == null ? "null" + s1);
tmp.append("Welcome to ");
tmp.append(s2 == null ? "null" + s2);
tmp.append("\n");
String test = tmp.toString();
(JIT 編譯器可以進一步優化,如果它可以推斷出 s1
或 s2
不能是這樣的話。)但是請注意,這種優化只允許在一個表示式中使用。
簡而言之,如果你擔心字串連線的效率:
- 如果你在迴圈(或類似)中重複連線,請進行手動優化。
- 不要手動優化單個連線表示式。