以数组实现。节约空间,但数组有容量限制。超出限制时会增加50%容量 ,用System.arraycopy()复制到新的数组,因此最好能给出数组大小的预估值。默认第一次插入元素时创建大小为10的数组 。 按照数组索引访问元素:get(int index)/set(int index)的性能很高,这是数组的优势。直接在数组末尾加入元素:add(e)的性能也高 ,但如果按索引插入、删除元素:add(i,e)、remove(i)、remove(e),则要用System.arraycopy()来移动部分受影响的元素 ,性能就变差了,这是数组的劣势。 ArrayList不是线程安全的,只能在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List list)方法返回一个线程安全的ArrayList对象,也可以使用concurrent并发包下的CopyOnWriteArrayList类。 ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上根据源码我们知道就是通过索引序号进行快速访问,实现了Cloneable接口,能被克隆。
概念
ArrayList扩容后的大小等于扩容前大小的1.5倍
扩容后将一直保存高水位线, 并不会因为remove而释放. 需要手动调用trimToSize()释放
默认最小初始化大小为10个
优缺点:
优点:
遍历速度快
论遍历ArrayList要比LinkedList快得多,ArrayList遍历最大的优势在于内存的连续性 ,CPU的内部缓存结构会缓存连续的内存片段,可以大幅降低读取内存的性能开销 。
缺点
扩容效率差
当ArrayList很大的时候,这样扩容还是挺浪费空间的,甚至会导致内存不足抛出OutOfMemoryError。扩容的时候还需要对数组进行拷贝,这个也挺费时的。所以我们使用的时候要竭力避免扩容 ,提供一个初始估计容量参数,以免扩容对性能带来较大影响。
重要参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
private static final int DEFAULT_CAPACITY = 10 ;private static final Object[] EMPTY_ELEMENTDATA = {};private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};transient Object[] elementData;private int size;private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ;protected transient int modCount = 0 ;
重要操作
构造函数
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
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); } } public ArrayList (Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0 ) { if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { this .elementData = EMPTY_ELEMENTDATA; } }
get
1 2 3 4 5 6 7 8 9
public E get (int index) { rangeCheck(index); return elementData(index); } E elementData (int index) { return (E) elementData[index]; }
set
1 2 3 4 5 6 7 8
public E set (int index, E element) { rangeCheck(index); E oldValue = elementData(index); elementData[index] = element; return oldValue; }
add
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; } public void add (int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1 ); System.arraycopy(elementData, index, elementData, index + 1 , size - index); elementData[index] = element; size++; }
remove
1 2 3 4 5 6 7 8 9 10 11 12 13 14
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; }
trimToSize 释放空间
1 2 3 4 5 6 7 8
public void trimToSize () { modCount++; if (size < elementData.length) { elementData = (size == 0 ) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
扩容机制
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
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); }