什麼情況下用+運算子進行字串連線比呼叫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 的要高一些。