ArrayList 是 List 接口最常用的通用实现。
概述:
底层由一个 Object 数组维护
构造时会设置一个初始的容量,元素增加时会自动扩容
必要时如元素为大对象,可手动缩容
线程不安全
常量与变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ... private static final int DEFAULT_CAPACITY = 10 ;private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};transient Object[] elementData; protected transient int modCount = 0 ; private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ;...
构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public ArrayList (Collection<? extends E> c) ;public ArrayList () { this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 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); } }
添加元素 & 动态扩容 ArrayList 添加元素前会确保容量足够,如不足则会进行扩容,初始容量 DEFAULT_CAPACITY = 10
。
添加元素 1 2 3 4 5 6 7 8 9 10 11 12 13 public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; }
动态扩容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 private void ensureCapacityInternal (int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity (Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity (int minCapacity) { modCount++; if (minCapacity - elementData.length > 0 ) { grow(minCapacity); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 private void grow (int minCapacity) { 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); }
其中,扩容关键语句是:
1 2 3 int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1 );
容量不足时,新容量为旧的 1.5 倍
位运算容量计算效率更高
1 2 int newCapacity = (oldCapacity * 3 )/2 + 1 ;
可以看到的是,扩容方法是通过将整个数组拷贝 的方式完成的。 因此要注意的是,对于大对象数组需考虑性能问题,提前规划容量,降低扩容频率。
总结如下:
获取元素 1 2 3 4 5 public E get (int index) { rangeCheck(index); return elementData(index); }
删除元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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 ; return oldValue; }
删除节点的关键 在于:删除某元素后,索引后元素的整体移动
1 System.arraycopy(elementData, index + 1 , elementData, index, numMoved);
因此 ArrayList 的删除性能需要考虑进元素移动 的时间复杂度。
与其它集合的比较 ArrayList v.s. Array
Array 可容纳基本类型和对象;ArrayList 只能容纳对象
Array 指定大小,ArrayList 大小固定
Array 的接口较少,但使用多维数组更方便
ArrayList v.s. Vector
两者均使用数组 方式存储数据(基于索引),数组元素数大于实际存储的数据以便增加和插入元素
两者均维护插入的顺序,都允许直接按照序号索引元素
但插入元素要涉及数组元素移动等内存操作:索引数据快,但插入数据慢
迭代器实现都是 fail-fast 的
Vector 由于使用 synchronized 方法(同步,线程安全),通常性能上较 ArrayList 差
寻求迭代时对列表进行改变:使用 CopyOnWriteArrayList