集合類源碼(三)Collection之List(CopyOnWriteArrayList, Stack)

CopyOnWriteArrayList

功能

全名

public class CopyOnWriteArrayList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

簡述

ArrayList的線程安全變體,其中所有的可變操作(添加、修改等)都是通過創建底層數組的新副本來實現的。

方法

// 返回列表裏元素的數量
public int size()

// 如果此列表不包含任何元素,則返回true。
public boolean isEmpty()

// 如果此列表包含至少一個指定的元素,則返回true。
public boolean contains(Object o)

// 返回該列表中指定元素第一次出現的索引,如果該列表不包含該元素,則返回-1。
public int indexOf(Object o)

// 返回該列表中指定元素第一次出現時的索引(從指定索引向前搜索),如果沒有找到該元素,則返回-1。
public int indexOf(E e, int index)

// 返回該列表中指定元素的最後一次出現的索引,如果該向量不包含該元素,則返回-1。
public int lastIndexOf(Object o)

// // 返回此列表中指定元素的最後一次出現的索引(從指定索引向後搜索),如果沒有找到該元素,則返回-1。
public int lastIndexOf(E e, int index)

// 返回該列表的淺拷貝。
public Object clone()

// 返回一個數組,該數組按適當的順序(從第一個元素到最後一個元素)包含列表中的所有元素。返回的數組將是“安全的”,因爲這個列表不維護對它的引用。(換句話說,這個方法分配一個新數組)。因此,調用者可以自由地修改返回的數組。
public Object[] toArray()

// 返回一個數組,該數組按適當的順序包含列表中的所有元素(從第一個元素到最後一個元素);返回數組的運行時類型是指定數組的運行時類型。
public <T> T[] toArray(T[] a)

// 返回列表中指定位置的元素。
public E get(int index)

// 將列表中指定位置的元素替換爲指定元素。
public E set(int index, E element)

// 將指定的元素追加到此列表的末尾。
public boolean add(E e)

// 將指定元素插入到列表中的指定位置。將當前位於該位置的元素和任何後續元素向右移動
public void add(int index, E element)

// 刪除列表中指定位置的元素。將任何後續元素向左移動。返回從列表中刪除的元素。
public E remove(int index)

// 從該列表中刪除指定元素的第一個匹配項
public boolean remove(Object o)

// 如果列表中沒有此元素,則添加
public boolean addIfAbsent(E e)

// 如果此列表包含指定集合的所有元素,則返回true。
public boolean containsAll(Collection<?> c)

// 從此列表中移除指定集合中包含的所有元素。因爲需要一個內部臨時數組,所以在這個類中這是一個特別昂貴的操作。
public boolean removeAll(Collection<?> c)

// 僅保留此列表中包含在指定集合中的元素。
public boolean retainAll(Collection<?> c)

// 將指定集合中尚未包含在此列表中的所有元素按照指定集合的迭代器返回它們的順序追加到此列表的末尾。
public int addAllAbsent(Collection<? extends E> c)

// 從列表中刪除所有元素。該調用返回後,列表將爲空。
public void clear()

// 將指定集合中的所有元素按照指定集合的迭代器返回它們的順序追加到此列表的末尾。
public boolean addAll(Collection<? extends E> c)

// 從指定位置開始,將指定集合中的所有元素插入此列表。
public boolean addAll(int index, Collection<? extends E> c)

// 爲可迭代的每個元素執行給定的操作,直到處理完所有元素或操作引發異常。
public void forEach(Consumer<? super E> action)

// 刪除此集合中滿足給定謂詞的所有元素。迭代期間或由謂詞拋出的錯誤或運行時異常將傳遞給調用者。
public boolean removeIf(Predicate<? super E> filter)

// 將此列表中的每個元素替換爲將操作符應用於該元素的結果。操作符拋出的錯誤或運行時異常將傳遞給調用者。
public void replaceAll(UnaryOperator<E> operator)

// 根據比較器產生的順序對這個列表進行排序。
public void sort(Comparator<? super E> c)

// 返回此列表的字符串表示形式。字符串表示由列表元素的字符串表示形式組成,其順序由其迭代器返回,包含在方括號中(“[]”)。
public String toString()

// 將指定的對象與此列表進行相等性比較。如果指定的對象是與此對象相同的對象,並且迭代器在指定列表上返回的元素序列與迭代器在此列表上返回的序列相同,則返回true。如果兩個序列長度相同,且序列中相同位置對應的元素相等,則認爲兩個序列是相同的。
public boolean equals(Object o)

// 返回此列表的哈希碼值。這個實現使用List.hashCode()中的定義。
public int hashCode()

// 按適當的順序對列表中的元素返回一個迭代器。
public Iterator<E> iterator()

// 返回該列表中元素的列表迭代器(按適當的順序)。
public ListIterator<E> listIterator()

// 返回此列表中元素的列表迭代器(按適當的順序),從列表中的指定位置開始。
public ListIterator<E> listIterator(int index)

// 在此列表中的元素上創建延遲綁定和快速失敗的Spliterator。
public Spliterator<E> spliterator()

// 返回該列表中fromIndex(包含)和toIndex(排除)之間部分的視圖。返回子列表中的更改將反映在此列表中。
public List<E> subList(int fromIndex, int toIndex)

原理

add

public boolean add(E e) {
    // 使用ReentrantLock保證了線程安全
    final ReentrantLock lock = this.lock;
    // 加鎖
    lock.lock();
    try {
        // 獲取當前數組
        Object[] elements = getArray();
        // 數組長度
        int len = elements.length;
        // 複製當前數組,新數組長度+1
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        // 設置新數據
        newElements[len] = e;
        // 將老數組替換成新數組
        setArray(newElements);
        return true;
    } finally {
        // 解鎖
        lock.unlock();
    }
}

addIfAbsent

public boolean addIfAbsent(E e) {
    // 獲取當前數組
    Object[] snapshot = getArray();
    // 先判斷當前數組中是否存在目標對象,存在則返回false,不做添加;否則添加。
    return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false :
        addIfAbsent(e, snapshot);
}
private boolean addIfAbsent(E e, Object[] snapshot) {
    // 獲取鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 獲取當前數組
        Object[] current = getArray();
        int len = current.length;
        // 如果當前數組和傳入的快照不是同一個對象,則代表數據發生了變化,需要確認
        if (snapshot != current) {
            // Optimize for lost race to another addXXX operation
            // 對另一個競爭失敗的addXXX操作進行優化
            // 得到兩份數據長度較小的一個
            int common = Math.min(snapshot.length, len);
            for (int i = 0; i < common; i++)
                // 如果與快照相比對應位置的數據不同,並且待添加對象和當前位置的數據相同,則返回false,停止add操作
                if (current[i] != snapshot[i] && eq(e, current[i]))
                    return false;
            // (前面循環沒有觸發條件)查看當前數組中是否存在目標對象,如果有即返回false,停止add操作
            if (indexOf(e, current, common, len) >= 0)
                    return false;
        }
        // 例行添加操作
        Object[] newElements = Arrays.copyOf(current, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

remove

public E remove(int index) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 獲取當前數組
        Object[] elements = getArray();
        int len = elements.length;
        // 用臨時變量保存老對象
        E oldValue = get(elements, index);
        // 計算要移動的元素
        int numMoved = len - index - 1;
        if (numMoved == 0)
            // 如果沒有要移動的,代表刪除了最後一個元素,只需要把末尾之前的數據複製到一份新數組即可
            setArray(Arrays.copyOf(elements, len - 1));
        else {
            // 創建一個新數組
            Object[] newElements = new Object[len - 1];
            // 把刪除位置之前的數據複製到新數組
            System.arraycopy(elements, 0, newElements, 0, index);
            // 把刪除位置之後的數據複製到新數組
            System.arraycopy(elements, index + 1, newElements, index,
                             numMoved);
            // 將老數組替換成新數組
            setArray(newElements);
        }
        // 返回刪除的對象
        return oldValue;
    } finally {
        lock.unlock();
    }
}

get

public E get(int index) {
    return get(getArray(), index);
}

優缺點

優點:根據數據結構和實現邏輯,這個適合“讀多寫少”的場景。

缺點:其一、因爲它添加和刪除都是要複製一份新數組,所以內存佔用是個問題;其二、因爲讀不加鎖,如果此時有add或者remove操作未完成,那麼在它讀的那一刻就決定了它拿到的是老數據。所以它只能保證數據的最終一致性,而不能保證實時一致性

Stack

功能

全名

public
class Stack<E> extends Vector<E>

簡述

Stack類表示對象的後進先出(LIFO)堆棧。它通過5個操作擴展了Vector類,這5個操作允許將一個Vector視爲一個堆棧。
Deque接口及其實現提供了一組更完整、更一致的後進先出堆棧操作,應該優先使用這些操作。例如:
Deque stack = new ArrayDeque();

方法

// 壓棧操作,將對象壓入堆棧頂部。與addElement(item)功能相同
public E push(E item)

// 出棧操作,刪除此堆棧頂部的對象並返回。
public E pop()

// 查看此堆棧頂部的對象,但不將其從堆棧中刪除。
public E peek()

// 查看堆棧是否爲空。
public boolean empty()

// 返回最接近堆棧頂部的搜索對象與堆棧頂部的距離;堆棧上最上面的對象被認爲與堆棧頂部的距離是1。
public int search(Object o)

原理

push

// push內部調用的是Vector中的同步方法addElement,其中的擴容邏輯在前面已經講過,這裏不再贅述
// 有一點需要注意的是,Stack的push,實際上是把新元素放在了Vector內部存儲結構的末尾。對應的pop(removeElementAt(len - 1))和search(int i = lastIndexOf(o))操作,也是從末尾開始的。
// 說白了,Stack將Vector的尾部當成棧頂。
public E push(E item) {
    addElement(item);

    return item;
}

public synchronized void addElement(E obj) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = obj;
}

pop

public synchronized E pop() {
    E       obj;
    int     len = size();

    // 先用臨時變量存儲棧頂元素
    obj = peek();
    // 然後移除棧頂元素
    removeElementAt(len - 1);

    return obj;
}
public synchronized void removeElementAt(int index) {
    // 內部結構修改次數 +1
    modCount++;
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }
    int j = elementCount - index - 1;
    if (j > 0) {
        // 將index之後的數據向前複製
        System.arraycopy(elementData, index + 1, elementData, index, j);
    }
    elementCount--;
    elementData[elementCount] = null; /* to let gc do its work */
}

peek

public synchronized E peek() {
    int     len = size();

    if (len == 0)
        throw new EmptyStackException();
    // 實際上調用Vector裏的elementAt方法
    return elementAt(len - 1);
}

public synchronized E elementAt(int index) {
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
    }
    // 直接返回下標位置的元素
    return elementData(index);
}

search

public synchronized int search(Object o) {
    // 從棧頂開始,找到目標第一次出現的位置
    int i = lastIndexOf(o);

    if (i >= 0) {
        // 返回與棧頂的距離
        return size() - i;
    }
    return -1;
}
public synchronized int lastIndexOf(Object o) {
    return lastIndexOf(o, elementCount-1);
}
public synchronized int lastIndexOf(Object o, int index) {
    if (index >= elementCount)
        throw new IndexOutOfBoundsException(index + " >= "+ elementCount);

    if (o == null) {
        // 從後向前搜索,找到第一個匹配的即返回
        for (int i = index; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        // 從後向前搜索,找到第一個匹配的即返回
        for (int i = index; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

優缺點

優點:實現了棧的數據結構,方便了開發;push和pop操作,都是發生在數組的末尾,沒有了元素的移動,提高了效率。

缺點:它是基於Vector的,操作都加了同步代碼,效率有所降低,畢竟有的情況下沒有線程安全問題。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章