主要 API
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| boolean add(Object)
void add(int,Object)
boolean addAll(Collection)
boolean addAll(int,Collection)
Object remove(int)
boolean remove(Object)
boolean removeIf(Predicate)
boolean removeAll(Collection)
List subList(int,int)
Object set(int,Object)
boolean retainAll(Collection)
void replaceAll(UnaryOperator)
Object get(int)
int indexOf(Object)
int lastIndexOf(Object)
boolean contains(Object)
boolean containsAll(Collection)
boolean isEmpty()
int size()
void forEach(Consumer)
void ensureCapacity(int)
void trimToSize()
Object clone()
void clear()
void sort(Comparator)
Object[] toArray()
Object[] toArray(Object[])
Iterator iterator()
Spliterator spliterator()
ListIterator listIterator(int)
ListIterator listIterator()
|
Java ArrayList的设计
凡是涉及到索引的操作前都会进行索引检查
凡是涉及添加元素的,添加前都会确保最小容量满足
只会自动扩容,不会自动缩容
扩容都是copy数组后返回一个更大容量的新数组
挪动数组是用native方法System.arraycopy()完成的
是线程不安全的
可以存储 null
添加元素到指定位置时的索引可以是最后一个元素的后面一个,即追加元素
无参构造默认容量为 10 但只有到第一次添加元素时才真正申请空间
默认最大容量为 231 - 1 - 8 实际最大容量为 231 - 1,减8的原因是,有的虚拟机的对象头会多占用八个位使int不能达到最大值 231 - 1
当一次性插入大量元素可以提前使用 ensureCapacity() 扩容来提高性能
六个主要的成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
transient Object[] elementData;
private int size;
|
扩容机制流程图
![ArrayList扩容原理.png ArrayList扩容原理.png]()
扩容的逻辑
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 47 48 49 50 51
| 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); }
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); }
private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError();
if((minCapacity > MAX_ARRAY_SIZE)){ return Integer.MAX_VALUE; }else { return MAX_ARRAY_SIZE; } }
|
ensureCapacity 确保容量的逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public void ensureCapacity(int minCapacity) { int minExpand;
if(elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA){ minExpand= 0; } else { minExpand = DEFAULT_CAPACITY; }
if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } }
|
batchRemove 批量删除的逻辑
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
|
private boolean batchRemove(Collection<?> c, boolean complement) { final Object[] elementData = this.elementData; int r = 0, w = 0; boolean modified = false; try { for (; r < size; r++){ if (c.contains(elementData[r]) == complement){ elementData[w++] = elementData[r]; } } } finally {
if (r != size) { System.arraycopy(elementData, r,elementData, w,size - r); w += size - r; }
if (w != size) {
for (int i = w; i < size; i++) elementData[i] = null;
modCount += size - w; size = w; modified = true; } } return modified; }
|
遍历时不能删除元素的原因
这是由于集合的快速失败机制导致的,因为 ArrayList 等集合是不支持同步操作的,当使用迭代器遍历元素的时候使用集合自带的API修改元素,快速失败机制认为你可能在使用多线程操作集合,马上抛出异常,但可以使用迭代器自带的 add() 和 remove() 等修改集合的元素个数。
快速失败机制主要是通过 modCount 和 expectedModCount 这两个变量完成的,遍历开始时初始化 expectedModCount = modCount,以后每遍历一个元素前都检查 modCount 是否等于 expectedModCount,如果不等于,说明集合被外部修了, 直接抛异常, 可以调用迭代器的 add() 和 remove()这两个方法,是因为迭代器内部可以自己使 modCount 和 expectedModCount 这两个变量的值相同。
但是快速失败机制并不是外部删除元素就百分之百会触发,有时候删除元素之后刚好迭代器遍历结束了,那么没有下一轮遍历,也就不会检测 modCount 和 expectedModCount 是否相等。