ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
一 自动扩容机制
Resizable-array implementation of the List interface.
capacity: 存储list元素的底层数组的大小;
list size:存储的list元素的个数;
-
初始化
不指明容量时,底层数组默认是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
,即{}
;
指明容量为0时,底层数组为EMPTY_ELEMENTDATA
,即{}
;
指明容量>0时,this.elementData = new Object[initialCapacity];
public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } // Constructs an empty list with an initial capacity of ten. public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
-
add
public boolean add(E e) { ensureCapacityInternal(size + 1); // 确保底层数组的容量 elementData[size++] = e; return true; }
先计算需要的容量,然后进行分配;
private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
计算所需的最小容量
如果开始没有指定容量大小,最低容量设为Math.max(10, minCapacity);
为什么没有指定容量时,不直接设为10呢?反序列化时也会调用,minCapacity可能>10;
如果开始制定了容量为0,最低容量是0;private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
底层数组容量是否改变,均执行modCount++; 表示list结构性变化;
private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
实际的数组扩增函数:1.5倍扩增
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //实际上小于MAX_ARRAY_SIZE,就报错OutOfMemoryError /** * Increases the capacity to ensure that it can hold at least the * number of elements specified by the minimum capacity argument. * * @param minCapacity the desired minimum capacity */ 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); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
ArrayList<Integer> list =new ArrayList<Integer>();
list.add(1);
默认数组分配容量为10 -
remove
remove函数并不会调节底层数组的大小;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; }
clear函数也并不会调节底层数组的大小;
/** * Removes all of the elements from this list. The list will * be empty after this call returns. */ public void clear() { modCount++; // clear to let GC do its work for (int i = 0; i < size; i++) elementData[i] = null; size = 0; }
真正减少底层数组容量的函数调用
trimToSize()
/** * Trims the capacity of this <tt>ArrayList</tt> instance to be the * list's current size. An application can use this operation to minimize * the storage of an <tt>ArrayList</tt> instance. */ public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
二: 线程不安全
This class is roughly equivalent to Vector, except that it is unsynchronized.
推荐方式:List list = Collections.synchronizedList(new ArrayList(...));
三: Fail-Fast 机制,ConcurrentModificationException
The iterators returned by this class's iterator methods are fail-fast:
if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own ListIterator.remove()
methods, the iterator will throw a ConcurrentModificationException
A structural modification is any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification. (内部int值modCount负责记录更改次数)
-
一旦创建了Iterator后,Iterator会记录当前list的modCount,并保存为expectedModCount;
此后外部list发生结构性更改,外部modCount更改,而内部expectedModCount不变,再次调用Iterator函数时无法通过检查,报错;private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
Iterator.remove
是删除前一次返回的值;
Iterator不能连续remove的原因,remove后 lastRet = -1; 解决方式:
3.1)Iterator创建之后,不能用ArrayList增删元素。 只能用Iterator删除元素;
因为Iterator.remove
会同步更新list的modCount以及expectedModCount;
subList 和Iterator 一样,创建后list就不能结构性修改了;list的增删不要Iterator的操作混在一起
3.2)java1.8后,貌似removeIf函数式编程也可以避免,待研究;
注意
ArrayList线程不安全,改为Vector
或者Collections.synchronizedList(list);
并不能避免ConcurrentModificationException。
因为线程安全是多线程排队访问list,不同时访问。仍然允许一个创建了Iterator,另一个修改,只要不是同时就没问题。
四: 总结
- ArrayList默认扩增方式为1.5倍;
- ArrayList随机访问get,set是0(1)时间开销,add有可能扩容,移动数组元素0(n)时间开销;删除移动数组元素0(n)时间开销;
Iterator.remove
移动数组元素0(n)时间开销;
数组优势,数组劣势 - list转array
List<String> v = new ArrayList<>(); return v.toArray(new String[v.size()]);
@梦工厂 2018.3.6