【collection】4.java容器之LinkedList,Stack,CopyOnWriteArrayList
阿新 • • 發佈:2022-12-13
LinkedList
節點資料結構
/**
* 泛型結構
* @param <E> node
*/
private static class Node<E> {
E item;
// 雙向連結串列,向前和向後
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
add
結論:新節點是插入到原來index的前面,原來index以及以後的節點,整體後移一位
/** * Returns the (non-null) Node at the specified element index. * 這裡索引用了二分的思想,但是不是二分的演算法 * 首先區分index是否小於一般,如果是,那麼從前往後找 * 如果大於一般,那麼從後往前找 */ Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { // 從first往後 Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { // 從last往前 Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } } /** * Inserts element e before non-null Node succ. */ void linkBefore(E e, Node<E> succ) { // assert succ != null; final Node<E> pred = succ.prev; final Node<E> newNode = new Node<>(pred, e, succ); // 斷開原來的前置連線線,並修改為新的 succ.prev = newNode; if (pred == null) { first = newNode; } else { // 斷開原來的後置,並更新 pred.next = newNode; } size++; modCount++; }
reomove,removeFirst,remove(index)
remove預設移除首節點,於removefirst作用相同
/** * Unlinks non-null first node f. */ private E unlinkFirst(Node<E> f) { // assert f == first && f != null; final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else { // 更新完first之後,這裡只需要把next.prev物件設定為null即可 next.prev = null; } size--; modCount++; return element; } /** * Unlinks non-null node x. */ E unlink(Node<E> x) { // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; // 前置節點為空,那麼直接把first移動到next if (prev == null) { first = next; } else { // 把前面節點的後置設定為下一個,跳過當前節點 prev.next = next; x.prev = null; } // 如果next本來是空的,那麼把last指標前移 if (next == null) { last = prev; } else { // 不為空,那麼把後面節點的前置指標跳過當前,設定前面一個節點 next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
remove(index)和 remove(Object)類似
但是remove(object)的意思是判斷空和非空,因為空的無法進行equals比較,迴圈查詢
另外remove(index)也是先根據node方法定位關聯節點
get,indexof查詢
get方法也是用node(index)定位,indexof方法:判斷空和非空,因為空的無法進行equals比較,迴圈查詢
參考
https://pdai.tech/md/java/collection/java-collection-LinkedList.html#queue-方法
Stack
棧的實現主要依賴的是vector
這裡主要是push和pop操作
擴容參考arraylist
push就是類似add,但是這裡的操作都加了synchronized關鍵字,所以stack是執行緒安全的
CopyOnWriteArrayList
add操作
public boolean add(E e) {
// 加鎖
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();
}
}
擴容
這個結構的擴容方式很簡單暴力
直接複製出來一份滿足要求大小的陣列
newElements = Arrays.copyOf(elements, len + 1);
get
沒有加鎖
private E get(Object[] a, int index) {
return (E) a[index];
}
總結:
資料一致性問題:CopyOnWrite容器只能保證資料的最終一致性,不能保證資料的實時一致性。
這句話的意思是在迴圈操作的過程中,這個get結果是不可知的,只能保證在set的時候沒問題,然後所有資料add完畢之後的結果符合預期
記憶體佔用問題:因為CopyOnWrite的寫時複製機制,所以在進行寫操作的時候,記憶體裡會同時駐紮兩個物件的記憶體,舊的物件和新寫入的物件(注意:在複製的時候只是複製容器裡的引用,只是在寫的時候會建立新物件新增到新容器裡,而舊容器的物件還在使用,所以有兩份物件記憶體)