ソースコード解析--ArrayList

序文

この記事では、我々は、Listインタフェースを実装し、別のコンテナクラス、ArrayListのを、分析、実装クラスがリストされ、基礎となる実装が配列で、私たちは以下に詳細に分析してみましょう。PS:いくつかの時間前など、あらゆる種類のものに加えて最終試験を設定し、学校の実験室のコースは、長い時間のためにブログを更新しなかったので...

1.概要

クラス図の間


ランダム・ArrayListクラスがインタフェースを実装して、ランダムアクセスをサポートしています。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
复制代码
デフォルトのアレイ10のサイズ、およびデータ記憶に見ることができるからelementDataこの配列。
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData;
private int size;复制代码

2.拡張

使用ensureCapacityInternal要素は十分に、あなたが拡大増殖()メソッドを使用する必要がない場合は、十分な容量を確保するために()メソッドを追加し、新しい容量の大きさがあるoldCapacity +(oldCapacity >> 1)、である 古いの1.5倍の容量
あなたがコピー操作、操作の高コストを横断している新しい配列に全体の元の配列をコピーするArrays.copyOf()を呼び出す必要があるとき、容量拡張操作のおおよそのサイズを指定するには、それは、ArrayListのオブジェクトを作成し事業拡大を軽減するのが最善です回数。

ことに留意すべきである操作の膨張はまた、スレッド安全でない動作です。国境を越えたアクセスの問題は、アレイは、マルチスレッド環境で表示されますので、この行のステートメントは、アトミック操作ではありません。だから、HashMapのと同様のArrayListは、スレッドセーフコンテナです

elementData[size++] = e;复制代码

以下は、新しい要素は、ソースコードと拡張操作中に挿入されます。

//插入新元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //这里会产生数组越界问题,因为下面这行语句并不是一个原子操作
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
//扩容
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //新容量为旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}复制代码

3.要素を削除します

+ 1つの要素はインデックス位置にコピーされたインデックスの後ろに()を呼び出す必要System.arraycopyの、動作 時間の複雑さはO(N)であり 、コストがArrayListの、要素を消去分かるは非常に高いです。

public E remove(int index) {
    rangeCheck(index);
    modCount++;
    E oldValue = elementData(index);
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work
    return oldValue;
}复制代码

4.フェイルファスト

約4.1は、フェイルファスト

フェイルファストつのエラー機構のメカニズムのJavaコレクション(コレクション)。複数のスレッドがコンテンツの同じセット上で動作している場合、それはフェイルファストイベントを発生させることができます。
たとえば、次のセットは、他のスレッドによって変更された場合、スレッドは収集プロセスを横断するイテレータ、コンテンツ場合、次に、スレッドAは、コレクションにアクセスし、例外がConcurrentModificationExceptionをスローするフェイルファストイベントを生成します。

フェイルファスト4.2 ArrayListの中

ArrayListのmodCountは、構造の変化の回数を記録するために使用しました操作、又は内部配列の構造内のすべての変更のサイズを調整するには追加または削除される少なくとも1つの要素を、要素の値だけではない構造の変化を設定することを意味します。

ArrayListのか、などの反復のシーケンスを実行するとき、あなたは前と変更するかどうかを操作modCount後に比較する必要がある 変更はConcurrentModificationExceptionを上げることができれば、。

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}复制代码

のシーケンス

ArrayListのアレイベースの実装と拡張ダイナミック特性、従って、必ずしも配列要素が使用されて格納されていない、すべてのシリアライズする必要はありません。
elementDataが配列でシリアライズされません宣言するために、デフォルトのキーワードの過渡的な変更を使用し、配列の要素を保存します。

transient Object[] elementData; // non-private to simplify nested class access复制代码
ArrayListのは、要素内容の部分を埋めるために持つ配列の配列を制御するためのwriteObject()とreadObject()を実装します。

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}复制代码
あなたは、変換するシリアライズのwriteObjectたObjectOutputStream()オブジェクトと出力バイトストリームを使用する必要があります。writeObject()メソッドは、オブジェクトの反射に行くときの着信オブジェクトに)(writeObjectメソッドを持っているシリアル化を達成するためのwriteObject()を呼び出します。ObjectInputStreamのreadObject()メソッド、シリアライゼーションおよびデシリアライゼーション同様の原理を用いてデシリアライズ。

ArrayList list = new ArrayList();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(list);复制代码

6.まとめ

ビューのソースポイントからのArrayList)は、いくつかの論文は、追加するいくつかの一般的に使用される方法、(と内部データ構造のArrayListを解析HashMapのより幾分単純である必要はない)(削除。それはまた、ArrayListの中でフェイルファストを紹介し、マルチスレッド環境でスレッドセーフなコンテナである理由あなたがマルチスレッド環境で使用したい場合は、ベクトルCopyOnWriteArrayListとか、二つの容器を使用することを検討して議論しましたアプリケーションとのArrayListのシリアル化の問題。

最後に、私はまた、この記事の欠点を指摘し、お互いに議論するあなたを歓迎します。


おすすめ

転載: juejin.im/post/5d29d66de51d45108c59a5f0