從記憶體方面解釋Java中String與StringBuilder的效能差異_java

來源:互聯網
上載者:User

以前經常在網上看到關於Java字串拼接等方面的討論。看到有些Java開發人員在給新手程式員的建議中類似如下寫道:

不要使用+號拼接字串,要使用StringBuffer或StringBuilder的append()方法來拼接字串。
不過,用+號拼接字串就真的那麼令人討厭,難道使用+號拼接字串就沒有一點可取之處嗎?

通過查閱Java API文檔中關於String類的部分內容,我們可以看到如下片段:
“Java 語言提供對字串串聯符號("+")以及將其他對象轉換為字串的特殊支援。字串串聯是通過 StringBuilder(或 StringBuffer)類及其 append 方法實現的。字串轉換是通過 toString 方法實現的,該方法由 Object 類定義,並可被 Java中的所有類繼承。”

這段話很明確地告訴我們,在Java中使用+號拼接字串,實際上使用的就是StringBuffer或StringBuilder及其append方法來實現的。

除了Java API文檔,我們還可以使用工具查看class類檔案的位元組碼命令來得到上述答案。 例如代碼:

public static void main(String[] args) {  String a = "Hello";  String b = " world";  String str = a + b + " !";  System.out.println(str);}

通過工具查看到其對應的位元組碼命令如下:

從位元組碼命令中,我們可以清楚地看到,我們編寫的如下代碼

String str = a + b + " !";

被編譯器轉換成了類似如下語句:

String str = new StringBuilder(String.valueOf(a)).append(b).append(" !").toString();

不僅如此,Java的編譯器也是一個比較聰明的編譯器,當+號拼接的全部是字串字面量時,Java的編譯器將會在編譯時間智能地將其轉換為一個完整的字串。例如:

public static void main(String[] args) {  String str = "Hello" + " world" + ", Java!";  System.out.println(str);}

Java編譯器直接將這種全是字面量的字串拼接,在編譯時間就轉換為了一個完整的字串。

就算+號拼接的字串中存在變數,Java編譯器也會將最前面的字串字面量合并為一個字串。

public static void main(String[] args) {  String java = ", Java!";  String str = "Hello" + " world" + java;  System.out.println(str);}

從上述可知,對於類似String str = str1 + str2 + str3 + str4,這種將多個字串一次性拼接的操作,使用+號來進行拼接是完全沒有問題的。

在Java中,String對象是不可變的(Immutable)。在代碼中,可以建立多個某一個String對象的別名。但是這些別名都是的引用是相同的。
比如s1和s2都是”droidyue.com”對象的別名,別名儲存著到真實對象的引用。所以s1 = s2

String s1 = "droidyue.com";String s2 = s1;System.out.println("s1 and s2 has the same reference =" + (s1 == s2));

並且在Java中,唯一被重載的運算子就是字串的拼接相關的。+,+=。除此之外,Java設計者不允許重載其他的運算子。

在Java中,唯一被重載的運算子就是字串的拼接相關的。+,+=。除此之外,Java設計者不允許重載其他的運算子。

眾所周知,在Java 1.4版本之前,字串拼接可以使用StringBuffer,從Java 1.5開始,我們可以使用StringBuilder來拼接字串。StringBuffer和StringBuilder的主要區別在於:StringBuffer是安全執行緒的,適用於多線程操作字串;StringBuilder是線程不安全的,適合單線程下操作字串。不過,我們的大多數字串拼接操作都是在單線程下進行的,因此使用StringBuilder有利於提高效能。

在Java 1.4之前,編譯器使用StringBuffer來處理+號拼接的字串;從Java 1.5開始,編譯器大多數情況下都使用StringBuilder來處理+號拼接的字串。

當我們在JDK 1.4的環境下編寫代碼時,對於上述這種一次性拼接多個字串的情況,建議最好使用+號來處理。這樣,當JDK 升級到1.5及以上版本時,編譯器將會自動將其轉換為StringBuilder來拼接字串,從而提高字串拼接效率。

當然,推薦使用+號拼接字串也僅限於在一條語句中拼接多個字串時使用。如果分散在多條語句中拼接一個字串,仍然建議使用StringBuffer或StringBuilder。 例如:

public static void main(String[] args) {  String java = ", Java!";  String str = "";  str += "Hello";  str += " world";  str += java;  System.out.println(str);}

編譯器編譯後的位元組命令如下:

從上面的圖片中我們可以知道,每一條+號拼接語句,都建立了一個新的StringBuilder對象。這種情況在迴圈條件下表現得尤其明顯,造成了相對較大的效能損耗。因此,在多條語句中拼接字串,強烈建議使用StringBuffer或StringBuilder來處理。

關於使用StringBuilder帶來的最佳化

此外,在使用StringBuffer或StringBuilder的時候,我們還可以使用如下方式,進一步提高效能(下面代碼以StringBuilder為例,StringBuffer與此類似)。

1.預測最終獲得的字串的最大長度。

StringBuilder內部char數組的預設長度為16,當我們append追加字串後超過此長度時,StringBuilder會擴大內部的數組容量以滿足需要。在這個過程中,StringBuilder會建立一個新的較大容量的char數組,並將原數組中的資料複製到新數組中。如果我們能夠大致預測到最終拼接得到的字串的最大長度,就可以在建立StringBuilder對象時指定合適大小的初始容量。例如,我們需要拼接獲得含有100個字母a的字串。即可編寫如下代碼:

StringBuilder sb = new StringBuilder(100);for (int i = 0; i < 100; i++) {  sb.append('a');}System.out.println(sb);

請根據實際情況進行平衡,以建立適合初始容量的StringBuilder。

2.對於單個字元,儘可能地使用char類型,而不是String類型。

有些時候,我們需要在字串後追加單個字元(例如:a),此時應儘可能地使用

sb.append('a');

而不是使用:

sb.append("a");

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.