Java集合List

170 阅读11分钟

本文基于JDK11。

List是Java集合中常用的一个接口,用来存放单一元素。存储的元素是有序的,可重复的。其常用的实现类有ArrayList,LinkedList,类结构如图所示。

image.png

image.png

可以看到,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,则创建一个大小为initialCapacityObject[]数组,如果容量等于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,则表明存在这个元素。