Java LinkedList雙向連結串列原始碼分析
LinkedList就傳說中的雙向連結串列了。是List 介面的連結列表實現。實現所有可選的列表操作,並且允許所有元素(包括 null)。除了實現 List 介面外,LinkedList 類還為在列表的開頭及結尾 get、remove 和 insert 元素提供了統一的命名方法。這些操作允許將連結列表用作堆疊、佇列或雙端佇列。
檢視LinkedList的建構函式:/** * Constructs an empty list. */ public LinkedList() { header.next = header.previous = header; }
這裡的header變數的定義如下:
private transient Entry<E> header = new Entry<E>(null, null, null);
表示雙向迴圈連結串列的頭結點。其中的Entry定義了雙向連結串列的結構:
private static class Entry<E> { E element; Entry<E> next; Entry<E> previous; Entry(E element, Entry<E> next, Entry<E> previous) { this.element = element; this.next = next; this.previous = previous; } }
Entry中包括資料元素、前續結點和後續結點。
建構函式即是初始化一個只包含header頭結點一個元素的雙向連結串列。
可以得出:LinkedList實質上就是一個雙向連結串列,並把header作為頭結點。 檢視LinkedList的add(E)方法:public boolean add(E e) { addBefore(e, header); return true; }
add(E)方法主要指向了addBefore(E, Entry)函式:
private Entry<E> addBefore(E e, Entry<E> entry) { Entry<E> newEntry = new Entry<E>(e, entry, entry.previous); newEntry.previous.next = newEntry; newEntry.next.previous = newEntry; size++; modCount++; return newEntry; }
這個方法中傳入的第二個引數entry為header頭結點,表示把元素插入到header頭結點之前。然後後兩句分別表示設定前續結點的後續結點和後續結點的前續結點。以便連線成雙向連結串列。
可以得出:LinkedList的add(E)方法把新結點插入到header頭結點之前,即列表的結尾。 檢視LinkedList的set(int, E)方法:/** * Replaces the element at the specified position in this list with the * specified element. * * @param index index of the element to replace * @param element element to be stored at the specified position * @return the element previously at the specified position * @throws IndexOutOfBoundsException {@inheritDoc} */ public E set(int index, E element) { Entry<E> e = entry(index); E oldVal = e.element; e.element = element; return oldVal; }
這個函式主要是設定某個索引位置的結點。首先呼叫了entry(int)方法:
/** * Returns the indexed entry. */ private Entry<E> entry(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size); Entry<E> e = header; if (index < (size >> 1)) { for (int i = 0; i <= index; i++) e = e.next; } else { for (int i = size; i > index; i--) e = e.previous; } return e; }
這裡首先判斷需要設定的位置是否越界,如果越界則丟擲異常。然後通過迴圈找到需要替換的結點。
接下來回到set(int ,E)函式,把舊的結點替換成新的結點,並返回舊的結點。
可以得出:set(int ,E)方法需要迴圈遍歷連結串列,時間開銷比較大。 檢視LinkedList的push(E)方法:/** * Pushes an element onto the stack represented by this list. In other * words, inserts the element at the front of this list. * * <p>This method is equivalent to {@link #addFirst}. * * @param e the element to push * @since 1.6 */ public void push(E e) { addFirst(e); }
push(E)方法主要呼叫了addFirst(E)方法:
/** * Inserts the specified element at the beginning of this list. * * @param e the element to add */ public void addFirst(E e) { addBefore(e, header.next); }
這裡繼續呼叫addBefore(E, Entity)表示把新節點插入到header頭結點之後。
可以得出:push(E)方法主要是把新節點插入到header頭結點之後。 檢視LinkedList的pop()方法:/** * Pops an element from the stack represented by this list. In other * words, removes and returns the first element of this list. * * <p>This method is equivalent to {@link #removeFirst()}. * * @return the element at the front of this list (which is the top * of the stack represented by this list) * @throws NoSuchElementException if this list is empty * @since 1.6 */ public E pop() { return removeFirst(); }
這裡繼續呼叫了removeFirst()方法:
/** * Removes and returns the first element from this list. * * @return the first element from this list * @throws NoSuchElementException if this list is empty */ public E removeFirst() { return remove(header.next); }
這裡繼續呼叫remove(Entry<E>)方法:
/** * Retrieves and removes the head (first element) of this list. * * @return the head of this list * @throws NoSuchElementException if this list is empty * @since 1.5 */ private E remove(Entry<E> e) { if (e == header) throw new NoSuchElementException(); E result = e.element; e.previous.next = e.next; e.next.previous = e.previous; e.next = e.previous = null; e.element = null; size--; modCount++; return result; }
在呼叫remove(Entry<E>)方法時,傳入了header的下一個結點。如果傳入的下一個結點是header的話,表示此事連結串列中只有一個結點header,此時丟擲異常。如果找到則刪除該節點(注意這裡雙向連結串列刪除結點時的步驟),並返回被刪除的結點。
可以得出:pop()方法刪除的是header頭結點之後的結點,並返回被刪除的結點。 ★ LinkedList實質上就是一個雙向連結串列,並把header作為頭結點。★ LinkedList的add(E)方法把新結點插入到header頭結點之前。
★ set(int ,E)方法需要迴圈遍歷連結串列,時間開銷比較大。
★ push(E)方法主要是把新節點插入到header結點之前
★ pop()方法刪除的是header頭結點之後的結點,並返回被刪除的結點。