今天在看LinkedList的源代碼的時候,遇到了一個坑。我研究源碼時,發現LinkedList是一個直線型的鏈表結構,但是我在baidu搜索資料的時候,關於這部分的源碼解析,全部都說LinkedList是一個環形鏈表結構。。我糾結了好長時間,還以為我理解錯了,最後還是在Google搜到了結果:因為我看的源碼是1.7的而baidu出來的幾乎全部都是1.6的。而且也沒有對應的說明。在1.7之後,oracle將LinkedList做了一些優化,將1.6中的環形結構優化為了直線型了鏈表結構。這裡要提示一下朋友們,看源碼的時候,一定要看版本,有的情況是屬於小改動,有的地方可能有大改動,這樣只會越看越迷糊。
好,言歸正傳。我們來分析一下Java中LinkedList的部分源碼。(本文針對的是1.7的源碼)
// 什麼都沒做,是一個空實現 public LinkedList() { } public LinkedList(Collection c) { this(); addAll(c); } public boolean addAll(Collection c) { return addAll(size, c); } public boolean addAll(int index, Collection c) { // 檢查傳入的索引值是否在合理范圍內 checkPositionIndex(index); // 將給定的Collection對象轉為Object數組 Object[] a = c.toArray(); int numNew = a.length; // 數組為空的話,直接返回false if (numNew == 0) return false; // 數組不為空 Node這裡要說明一下,Node是LinkedList的內部私有類,它的組成很簡單,只有一個構造方法。pred, succ; if (index == size) { // 構造方法調用的時候,index = size = 0,進入這個條件。 succ = null; pred = last; } else { // 鏈表非空時調用,node方法返回給定索引位置的節點對象 succ = node(index); pred = succ.prev; } // 遍歷數組,將數組的對象插入到節點中 for (Object o : a) { @SuppressWarnings("unchecked") E e = (E) o; Node newNode = new Node<>(pred, e, null); if (pred == null) first = newNode; else pred.next = newNode; pred = newNode; } if (succ == null) { last = pred; // 將當前鏈表最後一個節點賦值給last } else { // 鏈表非空時,將斷開的部分連接上 pred.next = succ; succ.prev = pred; } // 記錄當前節點個數 size += numNew; modCount++; return true; }
private static class Node構造方法的參數順序是:前繼節點的引用,數據,後繼節點的引用。 有了上邊的說明,我們來看LinkedList的構造方法。{ E item; Node next; Node prev; Node(Node prev, E element, Node next) { this.item = element; this.next = next; this.prev = prev; } }
public void addFirst(E e) { linkFirst(e); } private void linkFirst(E e) { final Node其實只要理解了上邊的數據結構,這段代碼是很好理解的。f = first; final Node newNode = new Node<>(null, e, f); // 創建新的節點,新節點的後繼指向原來的頭節點,即將原頭節點向後移一位,新節點代替頭結點的位置。 first = newNode; if (f == null) last = newNode; else f.prev = newNode; size++; modCount++; }
public E getFirst() { final Node這段代碼即不需要解析了吧。。很簡單的。f = first; if (f == null) throw new NoSuchElementException(); return f.item; } public E getLast() { final Node l = last; if (l == null) throw new NoSuchElementException(); return l.item; }
public E get(int index) { // 校驗給定的索引值是否在合理范圍內 checkElementIndex(index); return node(index).item; } Node一開始我很費解,這是要干嘛?後來我才明白,代碼要做的是:判斷給定的索引值,若索引值大於整個鏈表長度的一半,則從後往前找,若索引值小於整個鏈表的長度的一般,則從前往後找。這樣就可以保證,不管鏈表長度有多大,搜索的時候最多只搜索鏈表長度的一半就可以找到,大大提升了效率。node(int index) { if (index < (size >> 1)) { Node x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
public E removeFirst() { final Node摘掉頭結點,將原來的第二個節點變為頭結點,改變frist的指向,若之前僅剩一個節點,移除之後全部置為了null。f = first; if (f == null) throw new NoSuchElementException(); return unlinkFirst(f); } private E unlinkFirst(Node f) { // assert f == first && f != null; final E element = f.item; final Node next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }