字串連線運算子()

+符號可以表示 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 編譯器可以進一步優化,如果它可以推斷出 s1s2 不能是這樣的話。)但是請注意,這種優化只允許在一個表示式中使用。

簡而言之,如果你擔心字串連線的效率:

  • 如果你在迴圈(或類似)中重複連線,請進行手動優化。
  • 不要手動優化單個連線表示式。