1. 程式人生 > >什麼情況下用+運算子進行字串連線比呼叫StringBuilder物件的append方法連線字串效能更好?

什麼情況下用+運算子進行字串連線比呼叫StringBuilder物件的append方法連線字串效能更好?

java技術交流QQ群:83753349


經常在網上看到或者在周圍聽到有人說字串拼接不要直接用 String 相加, StringBuilder 的效率要比 String 直接相加拼接要高。還有人常說, StringBuffer 是同步的(執行緒安全的), StringBuilder 不是執行緒安全的,同步帶來了效能消耗,那麼 String 、 StringBuilder 、 StringBuffer 這三者的效率到底有多大的差距呢?

Talk is cheap, show me the code!

   
public class StringCatTest {
    public static void main(String[] args) {
        printResult(100);
        System.out.println("***********************************************");
        printResult(1000);
        System.out.println("***********************************************");
        printResult(10000);
        System.out.println("***********************************************");
        printResult(100000);
        System.out.println("***********************************************");
        printResult(1000000);
        System.out.println("***********************************************");
        printResult(10000000);



    }
    public static void printResult(long loopCount) {
        System.out.println("loopCount:" + loopCount);
        stringCat(loopCount);
        stringBuilderAppend(loopCount);
        stringBufferAppend(loopCount);
    }
    public static long stringCat(long loopCount) {
        long beginTime = System.currentTimeMillis();
        String str = "hello,world!";
        String result = "";

        for (int i = 0; i < loopCount; i++) {
            result += str;
        }
        long consumeTime = System.currentTimeMillis()-beginTime;
        System.out.println("String cat time:" + consumeTime);
        return consumeTime;
    }

    public static long stringBuilderAppend(long loopCount) {
        long beginTime = System.currentTimeMillis();
        String str = "hello, world!";
        String result = "";
        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < loopCount; i++) {
            stringBuilder.append(str);
        }
        result = stringBuilder.toString();
        long consumeTime = System.currentTimeMillis()-beginTime;
        System.out.println("StringBuilder append time:" + consumeTime);
        return consumeTime;

    }

    public static long stringBufferAppend(long loopCount) {
        long beginTime = System.currentTimeMillis();
        String str = "hello, world!";
        String result = "";
        StringBuffer stringBuffer = new StringBuffer();

        for (int i = 0; i < loopCount; i++) {
            stringBuffer.append(str);
        }
        result = stringBuffer.toString();
        long consumeTime = System.currentTimeMillis()-beginTime;
        System.out.println("StringBuffer append time:" + consumeTime);
        return consumeTime;
    }

}

很簡單的程式碼,通過調整迴圈次數的大小,來打印出三種字串連線所花費的時間:


從上面的輸出列印我們可以明顯地看出,在迴圈次數次數較小(比如說小於100)時,三者的時間效率差不多,當隨著迴圈次數的增加,對比效果發生了顯著的變化。

我們平時用字串直接相加作連線,也可以,但在迴圈內部最好還是用 StringBuilder 作字串的連線,其實在迴圈次數較小的情況下我們也可以用 String 直接來相加連線,但有時我們根本不能確定迴圈次數的大小,所以最好還是老老實實的用 StringBuilder 。

為什麼 StringBuilder 和 String 直接連線有如此大的效能差異呢,下面我們來分析一下:

        String str = "hello,world!";
        String result = "";

        for (int i = 0; i < loopCount; i++) {
            result += str;
        }

編譯器最終會把上面的程式碼編譯為類似下面的程式碼:

        String str = "hello,world!";
        String result = "";

        for (int i = 0; i < loopCount; i++) {
            result = new StringBuilder(result).append(str).toString();
        }

當執行 new StringBuilder(result) 時,構造方法 StringBuilder 會複製 result 中的所有字元到新建的 StringBuilder 中。每次迴圈都需要建立一個 StringBuilder 物件(建立物件時需要耗費時間和記憶體),隨著迴圈次數的增大, result 字串就會越來越長,把 result 中的字元複製到新建的 StringBuilder 中花費的時間也就越長,而且 StringBuilder(result).append(str).toString() 會建立一個臨時的字串,隨著迴圈次數的增加,這個操作花費的時間也會越來越長。總之,隨著迴圈變數 i 的增大,每次迴圈會變得越來越慢。

從上面的輸出可以看出當迴圈次數增大後, StringBuilder 的效率要比 StringBuffer要好一些。

下面是 StringBuilder 和 StringBuffer 兩者之間的一個對比:


當迴圈次數增大到 1000w後,兩者才有了明顯的差異, StringBuilder 的效率要比 StringBuffer 的要高一些。