文章出處

長久以來,我們被教導字符串的連接最好用StringBuffer、StringBuilder,但是我們卻不知道這兩者之間的區別.跟字符串相關的一些方法中總是有CharSequence、StringBuffer、StringBuilder、String,他們之間到底有什么聯系呢?

1、從類的定義看CharSequence、StringBuffer、StringBuilder、String的關系

下面先貼上這四者的定義(來自JDK1.6)

CharSequence是一個定義字符串操作的接口,StringBuffer、StringBuilder、String中都實現了這個接口.

//CharSequence定義
public interface CharSequence


//StringBuffer定義
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

//StringBuilder定義
public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence

//String定義
public final class String
    implements
java.io.Serializable, Comparable<String>, CharSequence

String 是java中的字符串,它繼承于CharSequence。
String類所包含的API接口非常多。為了便于今后的使用,我對String的API進行了分類,并都給出的演示程序。

String 和 CharSequence 關系
String 繼承于CharSequence,也就是說String也是CharSequence類型。
CharSequence是一個接口,它只包括length(), charAt(int index), subSequence(int start, int end)這幾個API接口。除了String實現了CharSequence之外,StringBuffer和StringBuilder也實現了CharSequence接口。
       也就是說,CharSequence其實也就是定義了字符串操作的接口,其他具體的實現是由String、StringBuilder、StringBuffer完成的,String、StringBuilder、StringBuffer都可以轉化為CharSequence類型。

StringBuilder StringBuffer 的區別

StringBuilder StringBuffer都是可變的字符序列。它們都繼承于AbstractStringBuilder,實現了CharSequence接口。
但是,StringBuilder是非線程安全的,而StringBuffer是線程安全的。

它們之間的關系圖如下:

 

 

2、從構造函數到具體的字符串拼接操作看看String、StringBuffer、StringBuilder的區別

下面我們來分析一下String、StringBuffer、StringBuilder具體的構造函數,了解他們是怎么構造出來的,再看看具體的字符串連接操作。

(1)String

String的構造函數(幾個常見的構造函數)

public String() {
        this.offset = 0;
        this.count = 0;
        this.value = new char[0];
    }

    /**
     * Initializes a newly created {@code String} object so that it represents
     * the same sequence of characters as the argument; in other words, the
     * newly created string is a copy of the argument string. Unless an
     * explicit copy of {@code original} is needed, use of this constructor is
     * unnecessary since Strings are immutable.
     *
     * @param  original
     *         A {@code String}
     */
    public String(String original) {
        int size = original.count;
        char[] originalValue = original.value;
        char[] v;

        if (originalValue.length > size) {
            // The array representing the String is bigger than the new
            // String itself.  Perhaps this constructor is being called
            // in order to trim the baggage, so make a copy of the array.
            int off = original.offset;
            v = Arrays.copyOfRange(originalValue, off, off + size);
        } else {
            // The array representing the String is the same
            // size as the String, so no point in making a copy.
            v = originalValue;
        }

        this.offset = 0;
        this.count = size;
        this.value = v;
    }

    /**
     * Allocates a new {@code String} so that it represents the sequence of
     * characters currently contained in the character array argument. The
     * contents of the character array are copied; subsequent modification of
     * the character array does not affect the newly created string.
     *
     * @param  value
     *         The initial value of the string
     */
    public String(char[] value) {
        this.offset = 0;
        this.count = value.length;
        this.value = StringValue.from(value);
    }

再看看String中具體的Concat函數

public String concat(String str) {
        int otherLen = str.length();

        if (otherLen == 0) {
            return this;
        }

        char[] buf = new char[count + otherLen];
        getChars(0, count, buf, 0);
        str.getChars(0, otherLen, buf, count);

        return new String(0, count + otherLen, buf);
    }

從Concat函數中,我們可以知道在對字符串使用concat操作后,具體的操作new出一個等同于兩個字符串連接總長度的新的char數組,然后將兩個字符串復制到新的char數組中,然后返回一個新的String對象。

(2)StringBuilder

StringBuilder常見構造函數

public StringBuffer() {
    super(16);
    }


    public StringBuffer(int capacity) {
    super(capacity);
    }

從StringBuilder的構造函數中,我們可以看見StringBuilder直接調用父類(AbstractStringBuilder)的構造函數,我們再看看AbstractStringBuilder的構造函數

abstract class AbstractStringBuilder implements Appendable, CharSequence {
    final static int[] sizeTable = {
            9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999,
            Integer.MAX_VALUE
        };

    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() {
    }

    /**
     * Creates an AbstractStringBuilder of the specified capacity.
     */
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
//其他的一些邏輯
}

從AbstractStringBuilder的構造函數中,我們可以看出StringBuilder中存儲字符串其實用的是一個char數組,capacity其實就是指定這個char數組的大小。

下面我們再從StringBuilder中的append函數看看他具體是怎么做的(以 append(String str) 為例看看)。

public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

又是直接調用父類(AbstractStringBuilder)的append方法,再跟到父類中去看看。

/**
     *  value 用來存儲字符串.
     */
    char value[];

    /** 
     * 有效字符串的數目.
     */
    int count;


    public AbstractStringBuilder append(String str) {
        if (str == null) {
            str = "null";
        }

        int len = str.length();
        if (len == 0) {
            return this;
        }

        int newCount = count + len;
        if (newCount > value.length) {
            expandCapacity(newCount);
        }
       //getChars將字符串復制到指定的位置
        str.getChars(0, len, value, count);
        count = newCount;
        return this;
    }

上面的邏輯還是比較簡單的,在append(str)函數調用的時候,首先會判斷原來用于存儲字符串的values的字符串數組有沒有足夠的大小來存儲將要新添加入StringBuilder的字符串。如果不夠用,那么就調用expandCapacity(int minimumCapacity)讓容量翻兩倍(一般是擴大兩倍,特殊情況見代碼),如果夠用,那么就直接添加進去。

/**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    void expandCapacity(int minimumCapacity) {
        int newCapacity = (value.length + 1) * 2;

        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
            newCapacity = minimumCapacity;
        }

        value = Arrays.copyOf(value, newCapacity);
    }

(3)StringBuffer

StringBuffer的構造函數

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

    /**
     * Constructs a string buffer with no characters in it and 
     * the specified initial capacity. 
     *
     * @param      capacity  the initial capacity.
     * @exception  NegativeArraySizeException  if the <code>capacity</code>
     *               argument is less than <code>0</code>.
     */
    public StringBuffer(int capacity) {
    super(capacity);
    }

StringBuffer也是直接調用父類(AbstractStringBuilder)的構造函數,那么我們從上面的分析中,就可以知道StringBuffer其實也是利用char[]類型的數組來保存字符串數組的。

再看看StringBuffer的append函數

public synchronized StringBuffer append(String str) {
    super.append(str);
        return this;
    }

還是調用父類的append函數,但是在這里有值得注意的地方,StringBuffer的append函數有一個synchronized標識符,也就是說StringBuffer中的append函數是線程安全的,通過繼續查閱其他StringBuffer中的函數,我們也可以發現他們有synchronized標識符,這就不難理解為什么StringBuffer是線程安全的,但是很明顯加上線程控制會拖慢程序運行的速度,所以如果不需要線程控制,那么最好就用StringBuilder。

//下面只是節選一些StringBuffer中的函數
synchronized StringBuffer     append(char ch)
synchronized StringBuffer     append(char[] chars)
synchronized StringBuffer     append(char[] chars, int start, int length)
synchronized StringBuffer     append(Object obj)
synchronized StringBuffer     append(String string)
synchronized StringBuffer     append(StringBuffer sb)
synchronized StringBuffer     append(CharSequence s)
synchronized StringBuffer     append(CharSequence s, int start, int end)
synchronized StringBuffer     insert(int index, char ch)
synchronized StringBuffer     insert(int index, char[] chars)
synchronized StringBuffer     insert(int index, char[] chars, int start, int length)
synchronized StringBuffer     insert(int index, String string)
StringBuffer     insert(int index, Object obj)

 

后記:

可能很多同學會想了解String中的+和StringBuilder.append的效率,以及糾結要用哪個。我在網上發現有人已經寫了一篇文章,分享給大家在Java中連接字符串時是使用+號還是使用StringBuilder

 

參考鏈接

在Java中連接字符串時是使用+號還是使用StringBuilder

String詳解, String和CharSequence區別, StringBuilder和StringBuffer的區別 (String系列之1)


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()