java.lang包—StringBuffer类和StringBuilder类
目錄
一、數據結構
二、線程安全性分析
三、源碼
四、適用場景
一、數據結構
在?Java?中字符串屬于對象,Java 提供了 String 類來創建和操作字符串。String 類是不可變類,即一旦一個 String 對象被創建以后,包含在這個對象中的字符序列是不可改變的,直至這個對象被銷毀。Java 提供了兩個可變字符串類 StringBuffer 和 StringBuilder,中文翻譯為“字符串緩沖區”。StringBuilder 類是 JDK 1.5 新增的類,它也代表可變字符串對象。實際上,StringBuilder 和 StringBuffer 功能基本相似,方法也差不多。不同的是,StringBuffer 是線程安全的,而 StringBuilder 則沒有實現線程安全功能,所以性能略高。因此在通常情況下,如果需要創建一個內容可變的字符串對象,則應該優先考慮使用 StringBuilder 類。StringBuffer、StringBuilder、String 中都實現了 CharSequence 接口。CharSequence 是一個定義字符串操作的接口,它只包括 length()、charAt(int index)、subSequence(int start, int end) 這幾個 API。StringBuffer、StringBuilder、String 對 CharSequence 接口的實現過程不一樣,如下圖 1 所示:
圖 1??對CharSequence接口的實現?
二、線程安全性分析
對待線程安全問題,我們可以把一個字符串的改變看成兩部來實現,
在單線程的情況下如果size等于0,那么添加一個元素的步驟就是將元素放在位置0,然后size=1,在單線程中這樣做是沒有任何問題的。但是多線程的話,線程a將元素放在位置0,此時的線程B也在搶奪CPU,假設搶奪成功, 那么這個時候a線程的size還沒有運行到增加的那一步 ,同時b線程在位置0又放入一個元素,這就回將a線程放入的元素覆蓋掉, 所以我們來看看實際的情況就是兩個元素都是放在位置0但是實際元素只有一個,size卻等于2,這就造成了線程不安全的問題。
三、源碼
1、StringBuffer
StringBuffer buffer= new StringBuffer(); buffer.append("d");進入append
@Override public synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);return this; }函數被synchronized修飾,說明是有線程安全的
2、StringBuilder
StringBuilder builder= new StringBuilder(); builder.append("1"); @Override public StringBuilder append(String str) {super.append(str);return this; }沒有synchronized,不是線程安全
3、擴展因子
public AbstractStringBuilder append(String str) {if (str == null)return appendNull();int len = str.length();ensureCapacityInternal(count + len);str.getChars(0, len, value, count);count += len;return this; }其實如果沒有synchronize的話,在count+=len不安全,所以如果并發比較大,盡量少用stringBuilder
四、適用場景
stringbuffer固然是線程安全的,stringbuffer固然是比stringbuilder更慢,固然,在多線程的情況下,理論上是應該使用線程安全的stringbuffer的。然而,然而,然而,有誰給我一個實際的案例來顯示你需要一個線程安全的string拼接器?對不起,至少在我淺薄的十幾年編程生涯中還沒有遇到過,也許,僅僅是也許,這個地球上的確是存在這樣的編程需求的,然而,它至少跟99.99...99%的程序員是無關的。所以,對于題主的問題,他們的適用場景是什么?最簡單的回答是,StringBuffer基本沒有適用場景,你應該在所有的情況下選擇使用StringBuiler,除非你真的遇到了一個需要線程安全的場景,如果遇到了,請務必在這里留言通知我。然后,補充一點,關于線程安全,即使你真的遇到了這樣的場景,很不幸的是,恐怕你仍然有99.99....99%的情況下沒有必要選擇StringBuffer,因為StringBuffer的線程安全,僅僅是保證jvm不拋出異常順利的往下執行而已,它可不保證邏輯正確和調用順序正確。大多數時候,我們需要的不僅僅是線程安全,而是鎖。
最后,為什么會有stringbuffer的存在,如果真的沒有價值,為什么jdk會提供這個類?答案太簡單了,因為最早是沒有stringbuilder的,sun的人不知處于何種愚蠢的考慮,決定讓stringbuffer是線程安全的,然后大約10年之后,人們終于意識到這是一個多么愚蠢的決定,意識到在這10年之中這個愚蠢的決定為java運行速度慢這樣的流言貢獻了多大的力量,于是,在jdk1.5的時候,終于決定提供一個非線程安全的stringbuffer實現,并命名為stringbuilder。順便,javac好像大概也是從這個版本開始,把所有用加號連接的String運算都隱式的改寫成StringBuilder,也就是說,從jdk1.5開始,用加號拼接字符串已經沒有任何性能損失了。
如諸多評論所指出的,我上面說,"用加號拼接字符串已經沒有任何性能損失了"并不嚴謹,嚴格的說,如果沒有循環的情況下,單行用加號拼接字符串是沒有性能損失的,java編譯器會隱式的替換成stringbuilder,但在有循環的情況下,編譯器沒法做到足夠智能的替換,仍然會有不必要的性能損耗,因此,用循環拼接字符串的時候,還是老老實實的用StringBuilder吧。
讀后有收獲可以支付寶請作者喝奶茶?
總結
以上是生活随笔為你收集整理的java.lang包—StringBuffer类和StringBuilder类的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java字符串常用操作
- 下一篇: 字符串类算法题