StringBuiderとStrigBufferについてどれぐらい知っていますか?


紹介します
1、StringBuiderとStrigBufferの同じ点
StringBuiderとStrigBufferは可変文字列のクラスを表しています。前節のStringは可変文字配列ではなく、String BullderとStrigBufferはAbstract StringBuiderを継承しています。序文化に関連する他の操作は父クラスAbstractStrigBuiderを呼び出す方法で実現しています。
2、StringBuiderとStrigBufferの違い
StrigBuiderはスレッド安全ではなく、シングルスレッド環境で使用されています。StringBurerはスレッド安全であり、マルチスレッド環境では、StringBufferの方法で、synchronizedはjavaに内蔵されています。複数のスレッドは同じStringBufferを操作する時にロックを取得します。Stringメソッドはキャッシュを使用しており、以下のコードを参照してください。
private transient char[] toStringCache;
    @Override
    public synchronized String toString() {
        if (toStringCache == null) {
            toStringCache = Arrays.copyOfRange(value, 0, count);
        }
        return new String(toStringCache, true);
    }
コードから、もしtostringCacheが空であれば、Arays.co pyOfRange(value,0,count)を呼び出して現在のStering Buffer内部配列のデータをコピーしてtostringCacheに値を付け、もしString Bufferオブジェクトが変化していなかったら、今度の呼び出しでコピーデータのオーバーヘッドを省き、直接toringCacheを使う。StringBufferオブジェクトに修正操作があるとtoStering Cacheを空にして、今度toSteringを呼び出してデータを再コピーします。読み取り操作はtoring Cacheを空にしません。スペースを置くということは、tostringCacheをインクリメントnulに引用し、toString方法にsynchronizedのキーワードを追加することも見られます。
二、コード解説
StringBuiderとStrigBufferのほとんどの操作は父の種類のAbstractStrigBuiderを呼び出す方法ですので、構造関数以外はAbstractStrigBuiderのコードだけを説明します。
StringBuider構造関数(StringBuffer類似)
  public StringBuilder() {
        super(16);
    }
参構造関数なしで、親構造関数を呼び出し、サイズ16の文字配列を作成します。
 public StringBuilder(int capacity) {
        super(capacity);
    }
指定されたサイズの文字配列を作成します。
    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

    public StringBuilder(CharSequence seq) {
        this(seq.length() + 16);
        append(seq);
    }
文字列または文字列はパラメータのコンストラクタとして、最初に親のコンストラクタを呼び出して文字列または文字列の長さを16に加えて、なぜ16を追加しますか?StringBuiderとStringBufferは可変文字列であるため、文字列を追加し続ける可能性が大きいです。したがって、元の長さに16を加えると内部char配列の拡大を回避できます。前提として追加した文字列は16.
AbstractStrigBuider
1、属性
char[] value;       
int count;       
2、構造方法
    //       
    AbstractStringBuilder() { 
    }
    //       char  
    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }
 
3、よく使う方法
public void ensureCapacity(int minimumCapacity)   char        minimumCapacity,    char      minimumCapacity,     ,         char      2 2    ,    char              。  minimumCapacity           。
   public void ensureCapacity(int minimumCapacity) {
        if (minimumCapacity > 0) //1
            ensureCapacityInternal(minimumCapacity);
    }

    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) { //2
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2; //3
        if (newCapacity - minCapacity < 0) { //4
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0) //5
            ? hugeCapacity(minCapacity)
            : newCapacity;
    }

   private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow  //6
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)  //7
            ? minCapacity : MAX_ARRAY_SIZE;
    }
1、指定されたminimum Capacityが0以下であれば、そのまま戻ってきます。
2、指定されたminimum Capacityは現在のchar配列容量より大きくて、newCapacityを呼び出して新しい容量を計算し、次にArays.co pyOfを呼び出して指定されたサイズの新しい配列をコピーし、現在のオブジェクトのchar配列に値を割り当て、新しい配列には元の配列のすべての値が含まれています。Arays.co pyOfはSystem.arraycopy方法を使用しています。前節で紹介しましたが、これ以上説明しません。
3、value.lengthは左に1桁移動して、2を乗じて、2をプラスして、拡大容量の配列サイズを計算して、なぜ2を乗じますか?拡張後の配列が大会を通過すると記憶空間の無駄が発生し、もし拡充後の配列が小さすぎると、配列の頻繁な拡充が性能に影響を与えるので、均衡のために二倍の拡充を選択し、二倍の拡充もコンピュータ分野でよく使われている。どうして後ろに2を追加しますか?valueのサイズが0の場合、2 newCapacityをプラスしないとサイズが0になり、拡張に失敗します。もちろん、どのような場合に現在のchar配列のサイズが0になるかという疑問がありますが、サブクラスの構造関数はサイズが16の配列を作成したのではないですか?キーはtrimToSizeメソッドで、現在のオブジェクトのcountが0なら、この方法を呼び出して現在の配列は0のサイズの配列に縮小されます。
4、現在のchar配列の大きさに応じて拡張したnewCapacityが指定のminCapacityより小さい場合、minCapacityを拡張した配列サイズとする。
5、newCapacityが0未満(オーバーフロー)またはnewCapacityがInteger.MAX_より大きい場合VALE-8は、指数拡張後のサイズまたは指定されたminCapacityが大きすぎると、hugeCapacity計算容量を呼び出し、さもなくば指数拡張後の容量に戻ります。
6、指定されたminCapacityはInteger.MAX_より大きいです。VALE、直接OOMを投げます。異常です。
7、minCapacityはInteger.MAX_より大きいです。VALE-8はミニCapacityに戻ります。さもなければInteger.MAX_に戻ります。VALE-8
public AbstractStringBuilder append(String str)                
    public AbstractStringBuilder append(String str) {
        if (str == null)  //1
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len); //3
        str.getChars(0, len, value, count); //4
        count += len;
        return this;
    }

    private AbstractStringBuilder appendNull() { //2
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }
他のappedの重載方法は大部分似ています。もう説明しません。
1、文字列をnullと指定すると、appendNullを呼び出して、「null」という四つの文字列をchar配列に追加します。何も追加していないわけではありません。
2、apendNull方法はまずensureCapacityInternalを呼び出して、現在の配列容量がcount+4サイズのデータを記憶できるかどうかを確認してから、現在のchar配列にnullの4文字を追加して、countを更新してから戻ります。
3、ensureCapacityInternalを呼び出して容量が指定文字列を十分に追加できるようにする
4、str.get Chars(0、len、value、count)を呼び出して、strのデータをvalueにコピーしてcountの先頭から、get Charrs方法内部もSystem.arraycopyを呼び出して、countを更新してから戻ります。
public AbstractStringBuilder insert(int offset, String str)           
    public AbstractStringBuilder insert(int offset, String str) {
        if ((offset < 0) || (offset > length()))  //1
            throw new StringIndexOutOfBoundsException(offset);
        if (str == null) //2
            str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len); //3
        System.arraycopy(value, offset, value, offset + len, count - offset); //4
        str.getChars(value, offset); //5
        count += len;
        return this;
    }
1、offsetパラメータが合法であるかどうかを確認し、不正な場合はインデックスの逸脱異常をスローする。
2、指定された文字列がnullである場合、文字列を「null」文字列に指定します。文字列は「null」文字列を追加します。
3、容量の大きさが十分であるかを確認する。
4、 System.arraycopyを呼び出して、valueのインデックスをoffset以降の文字列をoffset+lenにコピーします。lenは指定された文字列の長さで、offsetからoffset+lenまでのインデックスに指定された文字列strを格納します。
5、str.get Charsを呼び出して指定文字列strをvalueのインデックスoffsetにコピーし、valueのインデックスoffsetからoffset+lenに指定文字列strを格納し、countを更新してから戻ります。
AbstractStringBuilder delete(int start, int end)     start end     
    public AbstractStringBuilder delete(int start, int end) {
        if (start < 0)  //1
            throw new StringIndexOutOfBoundsException(start);
        if (end > count)
            end = count;
        if (start > end)
            throw new StringIndexOutOfBoundsException();
        int len = end - start;
        if (len > 0) {  //2
            System.arraycopy(value, start+len, value, start, count-end);
            count -= len;
        }
        return this;
1、startとendが合法的かどうかを判断する。
2、valのstart+lenインデックス後の文字列をstartインデックスにコピーし、startインデックスからstart+lenインデックスまでの文字列をstart+lenインデックス後の文字列で上書きし、相当と削除、countを更新して返します。なお、System.arraycopyはコピー配列中のデータだけであり、元の位置のデータはまだ変化していません。toStringの場合は、char配列のcount範囲内の文字列のみを取得します。
public AbstractStringBuilder reverse()       
 public AbstractStringBuilder reverse() {
        boolean hasSurrogates = false;
        int n = count - 1;
        for (int j = (n-1) >> 1; j >= 0; j--) { //1
            int k = n - j;
            char cj = value[j];
            char ck = value[k];
            value[j] = ck;
            value[k] = cj;
            if (Character.isSurrogate(cj) ||
                Character.isSurrogate(ck)) {
                hasSurrogates = true;
            }
        }
        if (hasSurrogates) { //2
            reverseAllValidSurrogatePairs();
        }
        return this;
    }

    /** Outlined helper method for reverse() */
    private void reverseAllValidSurrogatePairs() {
        for (int i = 0; i < count - 1; i++) { //3
            char c2 = value[i];
            if (Character.isLowSurrogate(c2)) {
                char c1 = value[i + 1];
                if (Character.isHighSurrogate(c1)) {
                    value[i++] = c1;
                    value[i] = c2;
                }
            }
        }
    }
1、Char配列を巡回して、jとkを計算して、jは中央桁数から左の第一文字に相当します。kは中央桁数の右側の文字に相当します。jを順番に減らして、kをインクリメントして、j所とk所の文字を交換して、char配列の中の文字の反転を完成します。hasSurrogates記録文字配列に補足文字の標識がありますか?二つ目のcharは低代理です。みんな自分で資料を調べます。
2、ハスSurrogatesはtrueで、char配列には補欠文字があり、reverseAllValidSurrogate Pairsを呼び出して増補文字を処理します。
3、Char配列を巡回して、現在の索引の下に補欠文字が存在する場合、このcharは反転後の補足文字であると説明しています。例えば、1234文字列が反転した後は4321で、12が補足文字である場合、反転して補欠文字の順序を変えるべきではないです。ロールオーバ前の補足文字は、高プロキシの前に、低プロキシの後に、反転後の低プロキシの前に、高プロキシの後にあるので、コードの中で、islowSurrogateはインデックス下のcharが低プロキシであるかどうかを判断します。
他の方法は大体似ています。または呼び出しの方法が実現します。もう説明しないで、次の文章でInteger類を説明します。