編碼如夢,望歸來還是老友。
LinkedList 是一個可能會經常被問到的集合類型,大多數都是和ArrayList進行對比,有什麼不同?啥啥啥的,但是真的和ArrayList相比的那些優勢有徹底或者更深入一步的瞭解嗎?主要弄明白鏈表和動態數組(數組)之前對保存數據和刪除數據和獲取數據之間的區別的時候,就會對這種問題的回答比較清晰。
來一張 LinkedList 的上層類的結構圖
這個圖主要是對上層比較清晰。
好啦,開始記錄一下看源碼的代碼心得了啦。
1 : 參數的說明
// 長度的變量 transient int size = 0;
// 第一個節點
transient Node<E> first;
// 最後一個節點
transient Node<E> last;
2 : 構造方法說明
// 無參構造方法,也就是什麼都沒有特別的做。 public LinkedList() { }
// 傳入集合的構造方法; 先調用上面的無參構造方法;然後調用addAll()方法
public LinkedList(Collection<? extends E> c) { this(); addAll(c); }
3 : 方法的介紹
// 檢查是否越界 private boolean isPositionIndex(int index) { return index >= 0 && index <= size; }
// 調用上面的方法進行判斷是否越界 private void checkPositionIndex(int index) { if (!isPositionIndex(index)) throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }
// 在鏈表的末尾插上元素 void linkLast(E e) { // 尾部的拿出來,用一個新的節點變量來進行存儲 final Node<E> l = last; // 用尾節點值 傳入進來的e值 用內部類來生成新對象Node final Node<E> newNode = new Node<>(l, e, null); // 新對象賦值給最後尾節點 last = newNode; // 如果之前尾節點是null的話,first節點給賦值上新的節點 if (l == null) first = newNode; else // 否則就指向下一個節點 l.next = newNode; // 長度 ++ size++; modCount++; }
// 傳入 E 值 和 一個節點 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節點指向新的節點 succ.prev = newNode; // succ 最初的prev判斷是否是null;如果是null的話,就賦值給first;否則的話,就用pred.next指向給新的節點 if (pred == null) first = newNode; else pred.next = newNode; // 長度 ++ size++; modCount++; } // 添加前判斷 checkPositionIndex 傳入進來的索引是否越界;然後根據index的大小判斷是否等於長度的大小,然後來調用不同的方法 public void add(int index, E element) { checkPositionIndex(index); if (index == size) linkLast(element); else linkBefore(element, node(index)); }
// node方法 Node<E> node(int index) { // assert isElementIndex(index); // size 右移1;然後判斷是否大於index if (index < (size >> 1)) { // 大於index 使用x 來儲存first節點信息 Node<E> x = first; // 遍歷,x -> x.next 讓自身的下一個節點賦值給自己 for (int i = 0; i < index; i++) x = x.next; return x; } else { // 用 x 存儲最後一個節點 Node<E> x = last; // 倒敘遍歷,返回prev節點的信息 for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
// set 方法相對於add 方法;就沒那麼複雜了 public E set(int index, E element) { // 先檢查index是否越界和符合要求 checkElementIndex(index); // 調用node方法來獲取Node信息 Node<E> x = node(index); // 獲取節點的值 oldVal E oldVal = x.item; x.item = element; return oldVal; } // 根據index 獲取值;先檢查index是否越界等;然後調用node()方法 這裏主要 node()是遍歷的;時間複雜度是O(n);而數組的話是直接根據下標獲取;時間複雜度是O(1)也就是常數 public E get(int index) { checkElementIndex(index); return node(index).item; }
public boolean remove(Object o) { if (o == null) { for (Node<E> x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node<E> x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }
// 獲取第一個節點的值;先將第一個節點值給 f ;然後判斷f 是不是null ; 如果是null 就拋出異常,如果不是的話;就調用iten屬性 public E getFirst() { final Node<E> f = first; if (f == null) throw new NoSuchElementException(); return f.item; }// 獲取最後一個節點的值 ; 操作思路是和 獲取首節點的值是一樣的 public E getLast() { final Node<E> l = last; if (l == null) throw new NoSuchElementException(); return l.item; }
4: 內部類說明
類 Node 就是存儲數據的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; } }還有其他的三個內部類;感興趣的話;可以自己研究下。