本文基于JDK11。
List是Java集合中常用的一个接口,用来存放单一元素。存储的元素是有序的,可重复的。其常用的实现类有ArrayList,LinkedList,类结构如图所示。
可以看到,LinkedList除了实现了List接口,还实现了Queue接口。
ArrayList
ArrayList继承于AbstractList,实现了List,RandomAccess,Cloneable,java.io.Serializable这些接口。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
- 继承了
AbstractList,实现List,表明这是一个列表,支持添加、删除、查找等操作。 - 实现了
RandomAccess接口。这是一个标记接口,用来指示ArrayList可以支持快速随机访问。通常,如果一个List的实现如果通过for循环遍历比通过迭代器遍历更快,则推荐实现这个接口。 - 实现类
Cloneable接口。这也是一个标记接口,表明可以调用Object.clone()方法实现对象的克隆。 Serializable,表明ArrayList可以进行序列化操作。
初始化
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
默认初始容量大小为10。
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] 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; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
EMPTY_ELEMENTDATA是一个空数组,适用于一个空实例。
DEFAULTCAPACITY_EMPTY_ELEMENTDATA是共享的空数组实例,用于默认大小(10)的空实例。我们将其与EMPTY_ELEMENTDATA区分开来,以便在添加第一个元素时知道需要扩展多少容量。
elementData是一个数组缓冲区,此数组缓冲区用于存储ArrayList中的元素。ArrayList的容量就是此数组缓冲区的长度。任何元素数据为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList,在添加第一个元素时都将扩展到DEFAULT_CAPACITY的大小。用transient关键字修饰,不需要序列化。
size表明这个ArrayList含有多少个元素。
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
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;
}
/**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
ArrayList有3种初始化的方式。
第一种是带参数的构造方法,自己指定容量,如果容量大于0,则创建一个大小为initialCapacity的Object[]数组,如果容量等于0,则创建一个空数组,如果容量小于0,则抛出异常。
第二种是无参构造方法,实际上初始化赋值了一个空数组,即上文提到的DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,在对这个数组添加元素时,会默认分配容量10。
第三种方式,参数是指定的collection元素列表,会将元素列表c中的所有元素转化为一个Object[]数组,并赋值给elementData。子类实现Collection时,会重写Collection.toArray()方法,返回的是子类实现的类型,而不是Object[]数组。因此,如果数组长度不为0(可以看到,size在这里赋值了),需要通过if (elementData.getClass() != Object[].class)判断,不相等时,要将c中的值copy到一个新的Object[]数组中。如果长度为0则和第一种方式相同,创建一个空数组。
添加元素
/**
* 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);
}
}
内部数组elementData扩容后,可能会有无用的空间,此方法用来压缩空间,将elementData的长度压缩为当前实际元素个数。
ArrayList在添加元素之前,需要保证数组有足够的容量,方法间的调用关系为>ensureCapacity()->grow()->newCapacity()->hugeCapacity()方法。
定义要分配的最大的数组大小为Integer类型最大值-8。
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
newCapacity()和hugeCapacity()方法结合,实现了ArrayList的扩容机制。
/**
* Returns a capacity at least as large as the given minimum capacity.
* Returns the current capacity increased by 50% if that suffices.
* Will not return a capacity greater than MAX_ARRAY_SIZE unless
* the given minimum capacity is greater than MAX_ARRAY_SIZE.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
newCapacity()方法参数minCapacity表示需要的最小容量。注释“ // overflow-conscious code”表明这段代码考虑了溢出的情况。oldCapacity表示原有的长度,oldCapacity >> 1是右移一位,等价于除以2,因此新的容量大约为旧容量的1.5倍(大数据情况下,右移比除法快)。检查新容量是否小于最小需要容量,如果小于,并且是第一次添加元素,则新容量为10,否则,新容量为最小需要容量。如果新容量大于最小需要的容量,检查新容量是否大于最大容量,如果小于,则新容量可以直接使用;如果大于,则进入hugeCapacity()方法,如果最小需要容量大于最大容量,则新容量为Integer.MAX_VALUE,否则最大容量为MAX_ARRAY_SIZE,即Integer.MAX_VALUE - 8。
简单来说,ArrayList扩容就是扩充成原长度的1.5倍。
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private Object[] grow() {
return grow(size + 1);
}
新增元素之前,会调用grow()进行扩容,如果是单个元素新增,会调用grow()方法,如果是批量新增,会直接调用grow(int minCapacity)。
/**
* This helper method split out from add(E) to keep method
* bytecode size under 35 (the -XX:MaxInlineSize default value),
* which helps when add(E) is called in a C1-compiled loop.
*/
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
elementData[index] = element;
size = s + 1;
}
对于单个元素的添加,ArrayList有添加到列表末尾和添加到指定位置两种方式。add(E e)将元素添加到列表尾部。add(E e, Object[] elementData, int s)是一个辅助函数,elementData是内部的动态数组,size是数组现有的个数,也是新元素要添加的位置。如果size == elementData.length,表明内部数组此时已经没有位置,则需要调用grow()进行扩容,再将元素e添加到指定位置,并将数组长度+1。
add(int index, E element)将元素添加到指定的位置index处。首先检查index是否在(0,size)之间,避免下标越界。之后,定义了一个局部变量s,用来存储当前列表的大小。if ((s = size) == (elementData = this.elementData).length) 检查当前的数组长度是否等于列表的大小。如果是,这意味着数组已满,需要调用grow()方法来扩容数组。之后,用System.arraycopy()方法将从index到size-1位置的元素移动的index+1到size位置,然后将新加的元素element加到指定位置index,并更新size大小。
/**
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the
* specified collection's Iterator. The behavior of this operation is
* undefined if the specified collection is modified while the operation
* is in progress. (This implies that the behavior of this call is
* undefined if the specified collection is this list, and this
* list is nonempty.)
*
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
System.arraycopy(a, 0, elementData, s, numNew);
size = s + numNew;
return true;
}
/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* @param index index at which to insert the first element from the
* specified collection
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
int numMoved = s - index;
if (numMoved > 0)
System.arraycopy(elementData, index,
elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size = s + numNew;
return true;
}
批量新增与单个元素的新增类似,提供了新增到list末尾和list指定位置两种。
addAll(Collection<? extends E> c)将元素添加到list末尾。初始化变量numNew表示要添加的元素个数,if (numNew > (elementData = this.elementData).length - (s = size))用来判断,当前元素个数size+要添加的元素个数numNew之和如果大于数组长度,则需要进行扩容。扩容后,将数组a的numNew长度复制到elementData的位置s之后。
addAll(int index, Collection<? extends E> c)将元素添加到指定位置。与增加单个元素不同的是,需要向后挪动numNew个位置,用来给c腾出空间。
删除元素
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
/**
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
remove(int index)方法用来删除指定位置的元素,首先,将原来的index位置的值保存到oldValue对象中,再调用了辅助方法fastRemove(Object[] es, int i),将从index+1到list末尾的值复制到从index开始的位置,并将list的size位置置为null,并更新size为newSize。最后返回被删除的值。
/**
* Private remove method that skips bounds checking and does not
* return the value removed.
*/
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
快速删除,并检查数组下表是否越界,也不返回删除的值。
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns.
*/
public void clear() {
modCount++;
final Object[] es = elementData;
for (int to = size, i = size = 0; i < to; i++)
es[i] = null;
}
clear()方法,从前往后遍历,删除所有的元素。
protected void removeRange(int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IndexOutOfBoundsException(
outOfBoundsMsg(fromIndex, toIndex));
}
modCount++;
shiftTailOverGap(elementData, fromIndex, toIndex);
}
/** Erases the gap from lo to hi, by sliding down following elements. */
private void shiftTailOverGap(Object[] es, int lo, int hi) {
System.arraycopy(es, hi, es, lo, size - hi);
for (int to = size, i = (size -= hi - lo); i < to; i++)
es[i] = null;
}
removeRange(int fromIndex, int toIndex)删除从fromIndex到toIndex的所有元素。先将toIndex开始一直到list末尾的元素复制到以startIndex开始的位置,再将fromIndex到toIndex的元素都置为null。
public boolean removeAll(Collection<?> c) {
return batchRemove(c, false, 0, size);
}
/**
* Retains only the elements in this list that are contained in the
* specified collection. In other words, removes from this list all
* of its elements that are not contained in the specified collection.
*
* @param c collection containing elements to be retained in this list
* @return {@code true} if this list changed as a result of the call
* @throws ClassCastException if the class of an element of this list
* is incompatible with the specified collection
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if this list contains a null element and the
* specified collection does not permit null elements
* (<a href="Collection.html#optional-restrictions">optional</a>),
* or if the specified collection is null
* @see Collection#contains(Object)
*/
public boolean retainAll(Collection<?> c) {
return batchRemove(c, true, 0, size);
}
boolean batchRemove(Collection<?> c, boolean complement,
final int from, final int end) {
Objects.requireNonNull(c);
final Object[] es = elementData;
int r;
// Optimize for initial run of survivors
for (r = from;; r++) {
if (r == end)
return false;
if (c.contains(es[r]) != complement)
break;
}
int w = r++;
try {
for (Object e; r < end; r++)
if (c.contains(e = es[r]) == complement)
es[w++] = e;
} catch (Throwable ex) {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
System.arraycopy(es, r, es, w, end - r);
w += end - r;
throw ex;
} finally {
modCount += end - w;
shiftTailOverGap(es, w, end);
}
return true;
}
查找元素
public int indexOf(Object o) {
return indexOfRange(o, 0, size);
}
int indexOfRange(Object o, int start, int end) {
Object[] es = elementData;
if (o == null) {
for (int i = start; i < end; i++) {
if (es[i] == null) {
return i;
}
}
} else {
for (int i = start; i < end; i++) {
if (o.equals(es[i])) {
return i;
}
}
}
return -1;
}
/**
* Returns the index of the last occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the highest index {@code i} such that
* {@code Objects.equals(o, get(i))},
* or -1 if there is no such index.
*/
public int lastIndexOf(Object o) {
return lastIndexOfRange(o, 0, size);
}
int lastIndexOfRange(Object o, int start, int end) {
Object[] es = elementData;
if (o == null) {
for (int i = end - 1; i >= start; i--) {
if (es[i] == null) {
return i;
}
}
} else {
for (int i = end - 1; i >= start; i--) {
if (o.equals(es[i])) {
return i;
}
}
}
return -1;
}
提供indexOf(Object o)和lastIndexOf(Object o)两个方法,indexOf从前向后遍历,lastIndexOf从后向前遍历,如果要找的是空,则用==比较,否则用equals比较,返回第一个遇到的下标。
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
如果使用indexOf(Object o)的结果大于0,则表明存在这个元素。