簡単な紹介
- ArrayListのアレイはに基づいて実施され、それは自動的にC言語ダイナミックアプリケーションメモリ、ダイナミックメモリの増加に類似する能力を増加させることができる動的配列です。
- ArrayListのは、スレッドセーフではない、シングルスレッド環境でのみ使用することができ、マルチスレッド環境ではスレッドセーフなArrayListクラスを返し、あなたがカテゴリの下に同時CopyOnWriteArrayListとし、契約を使用することができますCollections.synchronizedList(一覧リットル)の関数と考えることができます。
- ArrayListのは、Serializableインタフェースを実装し、それは、配列を介して送信の直列化をサポートRandomAccessインタフェースは、高速ランダムアクセス、実際にはインデックス番号がクローニングされ、Cloneableインタフェースを実現することにより、高速アクセスをサポートし実現します。
ストレージ構造
// 当前数据对象存放地方,当前对象不参与序列化
// 这个关键字最主要的作用就是当序列化时,被transient修饰的内容将不会被序列化
transient Object[] elementData;
オブジェクト型の配列。
データドメイン
// 序列化ID
private static final long serialVersionUID = 8683452581122892189L;
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 一个空数组,方便使用,主要用于带参构造函数初始化和读取序列化对象等。
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 和官方文档写的一样,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 和EMPTY_ELEMENTDATA 的区别
* 仅仅是为了区别用户带参为0的构造和默认构造的惰性初始模式对象。
* 当用户带参为0的构造,第一次add时,数组容量grow到1。
* 当用户使用默认构造时,第一次add时,容量直接grow到DEFAULT_CAPACITY(10)。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 当前数据对象存放地方,当前对象不参与序列化
// 这个关键字最主要的作用就是当序列化时,被transient修饰的内容将不会被序列化
transient Object[] elementData; // non-private to simplify nested class access
// 当前数组中元素的个数
private int size;
// 数组最大可分配容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 集合数组修改次数的标识(由AbstractList继承下来)(fail-fast机制)
protected transient int modCount = 0;
- 引数のコンストラクタをArrayListにありません。初期化時間は、実際に遅延初期化の対象である空間10を、作成されません。
- そして、デフォルトのコンフィギュレーションとコンフィギュレーション0に対する基準遅延初期化オブジェクトとユーザーを区別するDEFAULTCAPACITY_EMPTY_ELEMENTDATA EMPTY_ELEMENTDATAの違い。
- ArrayListのmodCountは、構造の変化の回数を記録するために使用しました。以下のためのフェイルファストメカニズム
コンストラクタ
public ArrayList() {
// 只有这个地方会引用DEFAULTCAPACITY_EMPTY_ELEMENTDATA
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 使用 EMPTY_ELEMENTDATA,在其他的多个地方可能会引用EMPTY_ELEMENTDATA
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList(Collection<? extends E> c) {
// 把传入集合传化成[]数组并浅拷贝给elementData
elementData = c.toArray();
// 转化后的数组长度赋给当前ArrayList的size,并判断是否为0
if ((size = elementData.length) != 0) {
//c.toArray可能不会返回 Object[],可以查看 java 官方编号为 6260652 的 bug
if (elementData.getClass() != Object[].class)
// 若 c.toArray() 返回的数组类型不是 Object[],则利用 Arrays.copyOf(); 来构造一个大小为 size 的 Object[] 数组
// 此时elementData是指向传入集合的内存,还需要创建新的内存区域深拷贝给elementData
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 传入数组size为零替换空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
- そして、デフォルトのコンフィギュレーションとコンフィギュレーション0に対する基準遅延初期化オブジェクトとユーザーを区別するDEFAULTCAPACITY_EMPTY_ELEMENTDATA EMPTY_ELEMENTDATAの違い。
- なお、深い浅いコピーコピーを。
参照が0に初期化し、不活性で構成され、0は遅延しない初期化が設定されていません。
追加()ソースコードを解析
public boolean add(E e) {
// 确保数组已使用长度(size)加1之后足够存下 下一个数据
ensureCapacityInternal(size + 1); // Increments modCount!!
// 数组的下一个index存放传入元素。
elementData[size++] = e;
// 始终返回true。
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 这里就是DEFAULTCAPACITY_EMPTY_ELEMENTDATA 和
// EMPTY_ELEMENTDATA 最主要的区别。
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 默认构造第一次add返回10。
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 带参为0构造第一次add返回 1 (0 + 1)。
return 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
newCapacity = minCapacity;
// 判断是需要的容量是否超过最大的数组容量。
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 在Arrays.copyOf()中会将原数组整个赋值到扩容的数组中。
elementData = Arrays.copyOf(elementData, newCapacity);
}
- 業務の拡大には新しい配列に全体の元の配列をコピーするArrays.copyOf()を呼び出す必要があり、この操作のコストが非常に高いので、それはあなたがArrayListのオブジェクトを作成するときに、拡張操作の数を減らし、おおよその容量サイズを指定するのがベストです。
(int型のインデックス、Eの要素)ソースコード解析を追加
// 这是一个本地方法,由C语言实现。
public static native void arraycopy(Object src, // 源数组
int srcPos, // 源数组要复制的起始位置
Object dest, // 目标数组(将原数组复制到目标数组)
int destPos, // 目标数组起始位置(从目标数组的哪个下标开始复制操作)
int length // 复制源数组的长度
);
public void add(int index, E element) {
// 判断索引是否越界
rangeCheckForAdd(index);
// 确保数组已使用长度(size)加1之后足够存下 下一个数据
ensureCapacityInternal(size + 1); // Increments modCount!!
// 运行到这里代表数组容量满足。
// 数组从传入形参index处开始复制,复制size-index个元素(即包括index在内后面的元素全部复制),
// 从数组的index + 1处开始粘贴。
// 这时,index 和 index + 1处元素数值相同。
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 把index处的元素替换成新的元素。
elementData[index] = element;
// 数组内元素长度加一。
size++;
}
- インデックスを含む呼び出すSystem.arraycopyの必要性()は、要素がバック位置指標+ 1にコピーされると、動作時間の複雑さはO(N)であり、それはArrayListのアレイヘッド素子はコストが非常に高い増加見ることができます。
削除(int型のインデックス)ソースコード解析
public E remove(int index) {
// 检查index
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
// 和 add(int index, E element)原理想通。
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 引用计数为0,会自动进行垃圾回收。
elementData[--size] = null; // clear to let GC do its work
// 返回旧元素
return oldValue;
}
- 呼び出すSystem.arraycopyの必要性は()のインデックス+ 1の要素は、操作時の複雑さはO(N)であり、インデックス位置にコピーされ、ArrayListのアレイヘッド素子はコストが非常に高い増加分かる後含まれます。
フェイルファストメカニズム
フェイルファストメカニズム、すなわち迅速な故障メカニズム、エラー検出機構のJavaコレクション(コレクション)インチ 時間の構造のセットの変更のコレクションの反復プロセスは、フェイルファストが発生する可能性があります場合には、すなわち、ConcurrentModificationExceptionが例外をスローします。フェイルファストメカニズムは、非同期の変更に保証するものではありません例外がスローされます、それだけで投げるために最善を尽くしので、このメカニズムは一般的にバグを検出するためにのみ使用されます。
- 配列や構造変化の内部サイズは、少なくとも一つの要素を追加または削除する意味調整するすべての操作は、構造においてだけではない変化要素の値を設定します。
- または反復の順序、必要な変更はConcurrentModificationExceptionを実行した場合、操作の前と後に変更するかどうかを比較的modCountを実行するとき
private class Itr implements Iterator<E> {
int cursor;
int lastRet = -1;
// 期待的修改值等于当前修改次数(modCount)
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
public E next() {
// 检查 expectedModCount是否等于modCount,不相同则抛出ConcurrentModificationException
checkForComodification();
/** 省略此处代码 */
}
public void remove() {
if (this.lastRet < 0)
throw new IllegalStateException();
checkForComodification();
/** 省略此处代码 */
}
final void checkForComodification() {
if (ArrayList.this.modCount == this.expectedModCount)
return;
throw new ConcurrentModificationException();
}
}
シングルスレッド環境でのフェイルファストの例
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0 ; i < 10 ; i++ ) {
list.add(i + "");
}
Iterator<String> iterator = list.iterator();
int i = 0 ;
while(iterator.hasNext()) {
if (i == 3) {
list.remove(3);
}
System.out.println(iterator.next());
i ++;
}
}
連載
ArrayListのは、java.io.Serializableのインタフェースを実装していますが、独自のシリアライズとデシリアライズを定義します。ArrayListのアレイベースの実装と動特性の膨張ので、必ずしもその後全く存在しない、配列要素が使用される保存されない全てのシリアル化する必要が。従って、過渡変形を使用してからelementDataアレイは、自動的にシリアル化されることを防止することができます。
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
// 将当前类的非静态(non-static)和非瞬态(non-transient)字段写入流
// 在这里也会将size字段写入。
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
// 序列化数组包含元素数量,为了向后兼容
// 两次将size写入流
s.writeInt(size);
// Write out all elements in the proper order.
// 按照顺序写入,只写入到数组包含元素的结尾,并不会把数组的所有容量区域全部写入
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
// 判断是否触发Fast-Fail
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// 设置数组引用空数组。
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
// 将流中的的非静态(non-static)和非瞬态(non-transient)字段读取到当前类
// 包含 size
s.defaultReadObject();
// Read in capacity
// 读入元素个数,没什么用,只是因为写出的时候写了size属性,读的时候也要按顺序来读
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
// 根据size计算容量。
int capacity = calculateCapacity(elementData, size);
// SharedSecrets 一个“共享机密”存储库,它是一种机制,
// 用于调用另一个包中的实现专用方法,而不使用反射。TODO
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, 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();
}
}
}
ArrayListのサイズは二回直列化されるのはなぜ?
コード内S.defaultWriteObjectは();サイズでも、なぜ我々は再び、単一のシリアル化されたの下に行くと、シリアル化する必要がありますか?
これは、互換性の理由のために書かれました。
JDKの古いバージョンでは、ArrayListのを実現する長さフィールドがシリアライズされ、異なっています。
JDKの新しいバージョンは、ArrayListのの実装ではなく、長さフィールドの順序を最適化します。
今回は、s.writeInt(サイズ)取り外した場合は、JDKシリアライズされたオブジェクトの新しいバージョンが古いバージョンで正しく読み取ることができない
ため、長さフィールドの欠如。
したがって、このようなアプローチは不要だが、実際に互換性を確保。
概要
- 制限能力なしに、方法の配列に基づいて、ArrayListの(膨張ん)
- あなたは要素を追加する(それが何を予測することが最善である)拡張する必要があり、(あなたが容量を小さくしたい場合trimToSize()を使用することができます)要素を除去する能力を低下させない、あなたが要素を削除すると、要素が削除される位置はnullに設定され、次のGCは、これらの要素によって占有されていたメモリ領域を解放します。
- スレッドセーフ
- アレイ位置、場所に指定された時間に要素を追加し、必要なすべての要素がコピーのピースの背後に戻って:(int型のインデックス、Eの要素)を追加
- (INTインデックス)を得る:要素に指定された場所を取得し、直接索引付けすることによって得ることができる(O(1))
- 反復処理する必要があります(オブジェクトo)を削除
- 削除(INTインデックス)単に反復する必要はないが、インデックス条件は、除去することができる効率的なよりか否かを判断する(オブジェクトo)
- 含まれている(E)は、反復処理する必要があります