1,ArrayList的秘密都在构造函数里
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
2,接着 elementData 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
翻译elementData的注释
存储ArrayList元素的数组缓冲区。
ArrayList的容量是这个数组缓冲区的长度。
添加第一个元素时,任何elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList都将扩展为默认的DEFAULT_CAPACITY容量。
3,接着DEFAULT_CAPACITY
/** * Default initial capacity. */ private static final int DEFAULT_CAPACITY = 10;
默认容量为10
4,看下添加第一个元素时发生了什么
/** * Appends the specified element to the end of this list. */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
确定内部容量,size作为成员变量private int size;,实例化时默认赋值为0,也就是用(0+1)1去确定内部容量。
size 加 1 ,并给索引位置赋值。
返回true。
这样添加就完成了,是否扩容并在下标位置赋值。
5,进入ensureCapacityInternal方法
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); ensureExplicitCapacity(minCapacity); }
如果是空数组,用传入的容量和默认的容量 之中 大的那一个作为初始化容量。也就是10个元素以内容量一直是10;
6,上面是初始化,下面才是真正的确定容量。进入ensureExplicitCapacity方法。
private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); }
修改次数 加1。这个参数用作并发修改控制。
如果需要的容量大于数组的长度,用需要的容量扩容。
7,扩容,grow()
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
数组的原容量oldCapacity
准备的容量oldCapacity + (oldCapacity >> 1);>>1向右位移1位,高位补0,即除以2,结果为1.5倍的原容量。
准备的容量不够,就用需要的容量。
准备的容量大于最大的数组容量,就取最小的大于需要的容量的容量。
原则就是实现最小扩容。
8,常问:从list里面移除元素
//从下标处移除元素,注意参数是int,如果是integer,会调用第二个方法。 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; } //移除指定元素,快速移除,跳过边界检查并且不返回已删除的值。 public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
第一个方法从下标处移除元素,注意参数是int,如果使用integer,会调用第二个方法。
第二个方法移除指定元素,移除的时候也是用的下标移除,但不是调的第一个方法,因为index一定是合法的而且移除的值是用户传进来的。也就是它跳过边界检查并且不返回已删除的值。
重点:list移除只需要调用remove方法,不需要使用循环遍历,迭代器,stream等实现。
9,ConcurrentModificationException
final void checkForComodification() { if (expectedModCount != ArrayList.this.modCount) throw new ConcurrentModificationException(); } @Override public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); final int expectedModCount = modCount; @SuppressWarnings("unchecked") final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
foreach时,final int expectedModCount = modCount;
modCount已确定且不可修改,而添加移除等方法中会调用checkForComodification方法,所以抛出异常。
10,fail-fast 快速失败
<p><a name="fail-fast"> * The iterators returned by this class's {@link #iterator() iterator} and * {@link #listIterator(int) listIterator} methods are <em>fail-fast</em>:</a> * if the list is structurally modified at any time after the iterator is * created, in any way except through the iterator's own * {@link ListIterator#remove() remove} or * {@link ListIterator#add(Object) add} methods, the iterator will throw a * {@link ConcurrentModificationException}. Thus, in the face of * concurrent modification, the iterator fails quickly and cleanly, rather * than risking arbitrary, non-deterministic behavior at an undetermined * time in the future.
Arraylist类73行定义,翻译为
这个类的iterator和listierator方法是快速失败的:如果迭代器被创建,不管何时何地,只要不是调用迭代器自己的remove或add方法,迭代器将抛出一个ConcurrentModificationException。因此如果是并发修改,迭代器会迅速而彻底地失败。翻译的不行,可自行翻译。
11,序列化
transient Object[] elementData;
可见elementData被transient 修饰,也就是不参与序列化,但是如果这个参数都不参与序列化,那反序列化的时候得到的不就是一个空列表吗,所以肯定是参与序列化了,下面看看它怎么被序列化的。