Java基礎(四) StringBuffer、StringBuilder原理淺析


StringBuilder與StringBuffer作用就是用來處理字符串,但String類本身也具備很多方法可以用來處理字符串,那么為什么還要引入這兩個類呢?

關於String的講解請看Java基礎(三) String深度解析

首先看下面的例子

public static void main(String[] args) {
    String str0 = "hel,lo,wor,l,d";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++){
        str0 += i;
    }
    System.out.println(System.currentTimeMillis() - start);

    StringBuilder sb = new StringBuilder("hel,lo,wor,l,d");
    long start1 = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++){
        sb.append(i);
    }
    System.out.println(System.currentTimeMillis() - start1);

    StringBuffer sbf = new StringBuffer("hel,lo,wor,l,d");
    long start2 = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++){
        sbf.append(i);
    }
    System.out.println(System.currentTimeMillis() - start2);
}

上述代碼中3處循環完成了同樣的功能,字符串拼接,執行的結果如下:

36823
3
4

可以看出執行時間差別太大,為了解決String不擅長的大量字符串拼接這種業務場景,引入了StringBuffer和StringBuilder.

首先我們分析一下為什么String在大量字符串拼接這種場景下這么慢?

因為String本身不可變,我們對String的任何操作都會返回一個新的對象,然后當前String變量指向新的對象,而原來的String對象就會被GC回收,那么在循環中就會大量快速的創建新的對象,大量原來的對象會不斷的被GC回收,消耗的時間是非常恐怖的,而且內存占用非常大。

下面我們對比了String、StringBuffer與StringBuilder的區別

String StringBuffer StringBuilder
final修飾,不可繼承 final修飾,不可繼承 final修飾,不可繼承
字符串常量,創建后不可變 字符串變量,可動態修改 字符串變量,可動態修改
不存在線程安全問題 線程安全,所有public方法由synchronized修改 線程不安全
大量字符串拼接效率最低 大量字符串拼接效率非常高 大量字符串拼接效率最高

StringBuffer與StringBuilder實現非常類似,下面以StringBuilder簡單說明一下append()方法基本原理

1. 首先創建一個StringBuilder

StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder(100);

StringBuilder對字符串的操作是通過char[]來實現的,通過默認構造器創建的StringBuilder,其內部創建的char[]的默認長度為16,當然可以調用重載的構造器傳遞初始長度(推薦這樣,因為這樣可以減少數組擴容次數,提高效率)。

/**
 * Constructs a string builder with no characters in it and an
 * initial capacity of 16 characters.
 */
public StringBuilder() {
    super(16);
}

2. StringBuilder的append()方法

每次調用append(str)方法時,會首先判斷數組長度是否足以添加傳遞來的字符串

/**
 * Appends the specified string to this character sequence.
 * <p>
 * The characters of the {@code String} argument are appended, in
 * order, increasing the length of this sequence by the length of the
 * argument. If {@code str} is {@code null}, then the four
 * characters {@code "null"} are appended.
 *
 * @param   str   a string.
 * @return  a reference to this object.
 */
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;
}
/**
 * For positive values of {@code minimumCapacity}, this method
 * behaves like {@code ensureCapacity}, however it is never
 * synchronized.
 * If {@code minimumCapacity} is non positive due to numeric
 * overflow, this method throws {@code OutOfMemoryError}.
 */
private void ensureCapacityInternal(int minimumCapacity) {
    // overflow-conscious code
    if (minimumCapacity - value.length > 0) {
        value = Arrays.copyOf(value,
                newCapacity(minimumCapacity));
    }
}

如果傳遞的字符串長度 + 數組已存放的字符的長度 > 數組的長度,這時就需要進行數據擴容了

/**
 * Returns a capacity at least as large as the given minimum capacity.
 * Returns the current capacity increased by the same amount + 2 if
 * that suffices.
 * Will not return a capacity greater than {@code MAX_ARRAY_SIZE}
 * unless the given minimum capacity is greater than that.
 *
 * @param  minCapacity the desired minimum capacity
 * @throws OutOfMemoryError if minCapacity is less than zero or
 *         greater than Integer.MAX_VALUE
 */
private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int newCapacity = (value.length << 1) + 2;
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
        ? hugeCapacity(minCapacity)
        : newCapacity;
}

擴容規則如下:默認將數組長度設置為“ (當前數組長度 * 2) + 2”,但如果按此規則擴容后的數組也不足以添加新的字符串,就需要將數組長度設置為“數組內字符長度 + 傳遞的字符串長度”。

因此假如我們知道拼接的字符串大概長度有100多字符,我們就可以設置初始長度150或200,這樣就可以避免或減少數組擴容的次數,從而提高效率。

總結:

本文StringBuffer與StringBuilder的創建,append方法的原理講解,對比了String、StringBuffer與StringBuilder異同,若有不對之處,請批評指正,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM