天天看點

Effective Java 第三版——63. 注意字元串連接配接的性能

Tips

書中的源代碼位址:https://github.com/jbloch/effective-java-3e-source-code

注意,書中的有些代碼裡方法是基于Java 9 API中的,是以JDK 最好下載下傳 JDK 9以上的版本。

63. 注意字元串連接配接的性能

字元串連接配接操作符(+)是将幾個字元串組合成一個字元串的便捷的方法。對于生成單行輸出或構造一個小的、固定大小的對象的字元串表示形式,它是可以的,但是它不能伸縮。重複使用字元串連接配接運算符來連接配接n個字元串需要n的平方級的時間。這是由于字元串是不可變的這一事實導緻的結果(條目 17)。當連接配接兩個字元串時,需要複制這兩個字元串的内容。

例如,考慮這個方法,它通過為每個項目重複連接配接一行來構造賬單語句的字元串表示:

// Inappropriate use of string concatenation - Performs poorly!
public String statement() {
    String result = "";
    for (int i = 0; i < numItems(); i++)
        result += lineForItem(i);  // String concatenation
    return result;
}
           

如果項的數量很大,則該方法的性能非常糟糕。為了達到可接受的性能,使用

StringBuilder

代替String來存儲正在建構的語句:

public String statement() {
    StringBuilder b = new StringBuilder(numItems() * LINE_WIDTH);
    for (int i = 0; i < numItems(); i++)
        b.append(lineForItem(i));
    return b.toString();
}
           

自Java 6以來,為了使字元串連接配接更快,已經做了大量工作,但是這兩個方法在性能上的差異仍然很大:如果numItems傳回100個元素,每個lineForItem傳回一個固定長度為80個字元串,那麼在我的機器上運作,第二個方法的速度是第一個方法的6.5倍。由于第一種方法在項目數量上是平方級增長的,而第二種方法是線性的,是以随着項目數量的增加,性能差異會變得越來越大。注意,第二個方法預先配置設定了一個足夠大的StringBuilder來儲存整個結果,進而消除了自動增長的需要。即使使用預設大小的StringBuilder,它仍然比第一個方法快5.5倍。

道理很簡單:除非性能無關緊要,否則不要使用字元串連接配接操作符組合多個字元串。而是使用StringBuilder的append方法。或者,使用字元數組,或者一次處理一個字元串,而不是把它們組合起來。