Java 8 ArrayList 詳解


GitHub Page: http://blog.cloudli.top/posts/Java-ArrayList/

ArrayList 繼承於 AbstractList ,實現了 List、RandomAccess、Cloneable、Serializable 接口。

ArrayList 的底層數據結構是數組,元素超出容量時會進行擴容操作。

ArrayList 中的屬性

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;
  • DEFAULT_CAPACITY:默認容量為 10。
  • EMPTY_ELEMENTDATA:通過構造方法指定的容量為 0 時,使用該空數組。
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA:調用無參構造方法時,使用該數組。
  • elementData:保存添加的元素,在 ArrayList 被序列化時,該屬性不會被序列化。
  • size:ArrayList 保存的元素個數。
  • MAX_ARRAY_SIZE:ArrayList 能夠容納的最大長度,231 - 1 - 8。

ArrayList 的構造方法

  • ArrayList():構造一個容量為 10 的空列表。
  • ArrayList(int initialCapacity):構造一個指定容量的空列表。
  • ArrayList(Collection<? extends E> c):構造一個列表,將給定集合中的元素按照迭代器返回的順序復制到列表中。

使用無參構造方法創建 ArrayList 時,elementData 的長度是 0,當第一次添加元素時,它會擴容到 DEFAULT_CAPACITY

使用指定容量構造 ArrayList 時,elementData 的長度為指定的長度。

ArrayList()

/**
 * Constructs an empty list with an initial capacity of ten.
 * 構造一個容量為 10 的空列表,當第一次添加元素時,elementData 會擴容到 10。
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

ArrayList(int initialCapacity)

/**
 * 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      指定容量 < 0 時拋出異常
 */
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(Collection<? extends E> c)

/**
 * 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;
    }
}

集合轉換為數組后,如果不是 Object[] 類型,會使用 Arrays.copyOf 方法返回一個新的 Object 數組。

元素操作的基本方法

方法名 說明 時間復雜度
E get(int index) 返回指定索引的元素 O(1)
E set(int index, E element) 替換指定索引的元素,返回舊元素 O(1)
boolean add(E e) 向列表的末尾添加元素 O(1)
void add(int index, E element) 在指定位置插入元素 O(N)
E remove(int index) 刪除指定位置的元素,返回舊元素 O(N)
boolean remove(Object o) 刪除列表中與給定對象相同的元素 O(N)
boolean contains(Object o) 判斷列表中是否包含指定元素 O(N)
int indexOf(Object o) 返回指定元素在列表中的索引 O(N)

其中,contains 方法直接 返回 indexOf(o) >= 0 。

對列表操作的方法

clear()

清空列表,將所有元素的引用指向 null,等待垃圾回收器回收,此操作不減小數組容量。

public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

subList(int fromIndex, int toIndex)

返回列表的指定區域(子列表),對返回的列表做修改會影響整個列表。

public List<E> subList(int fromIndex, int toIndex) {
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);
}

迭代器方法

iterator()

返回一個迭代器,如果在迭代器遍歷過程中調用了列表的 add,remove 等方法,會拋出 ConcurrentModificationException

public Iterator<E> iterator() {
    return new Itr();
}

listIterator()

返回一個迭代器,該迭代器可以向前后兩個方向遍歷元素。ListIter 繼承於 Iter 。

public ListIterator<E> listIterator() {
    return new ListItr(0);
}

listIterator(int index)

返回一個從指定位置開始的迭代器。

public ListIterator<E> listIterator(int index) {
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}

擴容方法

ArrayList 有兩個與擴容有關的方法:

  • void grow(int minCapacity)
  • int hugeCapacity(int minCapacity)

grow(int minCapacity)

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

minCapacity 為添加新元素后數組的長度(此時還未添加)。

首先將新長度(newCapacity)設置為原來的 1.5 倍,然后計算新的長度是否能夠達到 minCapacity,如果不能,則把新長度設置為 minCapacity 的大小。

如果新長度比 ArrayList 能夠容納的最大長度還要大,調用 hugeCapacity 方法來計算。否則,將列表長度調整為新長度。

hugeCapacity(int 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;
}

這里為什么要判斷 minCapacity < 0 ?

假如列表中已經有 oldCapacity = 231 - 1 個元素,在添加新元素的時候,minCapacity 為原來的長度 + 1,溢出變為 minCapacity = -231,newCapacity 擴大到 1.5 倍也溢出為負數(絕對值 < minCapacity),此時 grow 方法中 newCapacity - minCapacity > 0(絕對值小的負數 - 絕對值大的負數),最后調用該方法時傳遞的 minCapacity 就是 -231,將拋出異常。

最后如果 minCapacity 大於 ArrayList 能容納的最大長度,就返回整型最大值,否則返回 MAX_ARRAY_SIZE

函數式接口方法

ArrayList 有 4 個參數為函數式接口的方法:

1. forEach(Consumer<? super E> action)

消費列表中的元素,可以對列表中的元素作出處理。以下代碼打印列表中所有的偶數:

ArrayList<Integer> list = ...;

list.forEach(e -> {
    if (e % 2 == 0)
        System.out.printf("%d ", e);
});

2. removeIf(Predicate<? super E> filter)

刪除列表中滿足給定條件的元素。以下代碼刪除列表中所有偶數元素:

list.removeIf(e -> {
    return e % 2 == 0;
});

也可以這樣寫:

list.removeIf(e -> e % 2 == 0);

3. replaceAll(UnaryOperator operator)

替換列表中滿足給定條件的元素。以下代碼將所有偶數元素替換為 0 :

list.replaceAll(e -> {
    return e % 2 == 0 ? 0 : e;
});

也可以這樣寫:

list.replaceAll(e -> e % 2 == 0 ? 0 : e);

4. sort(Comparator<? super E> c)

使用給定的規則對列表元素排序。如果元素的類型已經實現了 Comparable 接口,可以直接傳遞方法引用。

以下代碼對列表進行升序排序:

list.sort(Integer::compareTo);

逆序可以使用 Comparator.reverseOrder :

list.sort(Comparator.reverseOrder());

也可以在 lambda 表達式中寫判斷邏輯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM