1 public class LinkedList<E> 2 extends AbstractSequentialList<E> 3 implements List<E>, Deque<E>, Cloneable, java.io.Serializable
能夠看出LinkedList 繼承AbstractSequentialList 抽象類,實現了List,Deque,Cloneable,Serializable 幾個接口,AbstractSequentialList 繼承 AbstractList,是對其中方法的再抽象,其主要做用是最大限度地減小了實現受「連續訪問」數據存儲(如連接列表)支持的此接口所需的工做,簡單說就是,若是須要快速的添加刪除數據等,用AbstractSequentialList抽象類,如果須要快速隨機的訪問數據等用AbstractList抽象類(詳細說明會在iterator 分析中進行解釋)。html
1 public interface Deque<E> extends Queue<E> { 2 void addFirst(E e); 3 boolean offerFirst(E e); 4 boolean offerLast(E e); 5 E removeFirst(); 6 E removeLast(); 7 E pollFirst(); 8 E pollLast(); 9 E getFirst(); 10 E getLast(); 11 E peekFirst(); 12 E peekLast(); 13 boolean removeFirstOccurrence(Object o); 14 boolean removeLastOccurrence(Object o); 15 // *** Queue methods *** 16 boolean add(E e); 17 boolean offer(E e); 18 E remove(); 19 E poll(); 20 E element(); 21 E peek(); 22 // *** Stack methods *** 23 void push(E e); 24 E pop(); 25 // *** Collection methods *** 26 boolean remove(Object o); 27 boolean contains(Object o); 28 public int size(); 29 Iterator<E> iterator(); 30 Iterator<E> descendingIterator(); 31 }
3.底層存儲java
1 private transient Entry<E> header = new Entry<E>(null, null, null); 2 private transient int size = 0;
LinkedList中提供了上面兩個屬性,其中size和ArrayList中同樣用來計數,表示list的元素數量,而header則是鏈表的頭結點,Entry則是鏈表的節點對象。設計模式
1 private static class Entry<E> { 2 E element; // 當前存儲元素 3 Entry<E> next; // 下一個元素節點 4 Entry<E> previous; // 上一個元素節點 5 6 Entry(E element, Entry<E> next, Entry<E> previous) { 7 this.element = element; 8 this.next = next; 9 this.previous = previous; 10 } 11 }
1 /** 2 * 構造一個空的LinkedList . 3 */ 4 public LinkedList() { 5 //將header節點的前一節點和後一節點都設置爲自身 6 header.next = header. previous = header ; 7 } 8 9 /** 10 * 構造一個包含指定 collection 中的元素的列表,這些元素按其 collection 的迭代器返回的順序排列 11 */ 12 public LinkedList(Collection<? extends E> c) { 13 this(); 14 addAll(c); 15 }
1 public LinkedList() { 2 header.next = null; 3 header. previous = null; 4 }
1 /** 2 * 將一個元素添加至list尾部 3 */ 4 public boolean add(E e) { 5 // 在header前添加元素e,header前就是最後一個結點啦,就是在最後一個結點的後面添加元素e 6 addBefore(e, header); 7 return true; 8 } 9 /** 10 * 在指定位置添加元素 11 */ 12 public void add(int index, E element) { 13 // 若是index等於list元素個數,則在隊尾添加元素(header以前),不然在index節點前添加元素 14 addBefore(element, (index== size ? header : entry(index))); 15 } 16 17 private Entry<E> addBefore(E e, Entry<E> entry) { 18 // 用entry建立一個要添加的新節點,next爲entry,previous爲entry.previous,意思就是新節點插入entry前面,肯定自身的先後引用, 19 Entry<E> newEntry = new Entry<E>(e, entry, entry.previous); 20 // 下面修改newEntry的先後節點的引用,確保其鏈表的引用關係是正確的 21 // 將上一個節點的next指向本身 22 newEntry. previous.next = newEntry; 23 // 將下一個節點的previous指向本身 24 newEntry. next.previous = newEntry; 25 // 計數+1 26 size++; 27 modCount++; 28 return newEntry; 29 }
1 /** 2 * 添加一個集合元素到list中 3 */ 4 public boolean addAll(Collection<? extends E> c) { 5 // 將集合元素添加到list最後的尾部 6 return addAll(size , c); 7 } 8 9 /** 10 * 在指定位置添加一個集合元素到list中 11 */ 12 public boolean addAll(int index, Collection<? extends E> c) { 13 // 越界檢查 14 if (index < 0 || index > size) 15 throw new IndexOutOfBoundsException( "Index: "+index+ 16 ", Size: "+size ); 17 Object[] a = c.toArray(); 18 // 要插入元素的個數 19 int numNew = a.length ; 20 if (numNew==0) 21 return false; 22 modCount++; 23 24 // 找出要插入元素的先後節點 25 // 獲取要插入index位置的下一個節點,若是index正好是lsit尾部的位置那麼下一個節點就是header,不然須要查找index位置的節點 26 Entry<E> successor = (index== size ? header : entry(index)); 27 // 獲取要插入index位置的上一個節點,由於是插入,因此上一個點擊就是未插入前下一個節點的上一個 28 Entry<E> predecessor = successor. previous; 29 // 循環插入 30 for (int i=0; i<numNew; i++) { 31 // 構造一個節點,確認自身的先後引用 32 Entry<E> e = new Entry<E>((E)a[i], successor, predecessor); 33 // 將插入位置上一個節點的下一個元素引用指向當前元素(這裏不修改下一個節點的上一個元素引用,是由於下一個節點隨着循環一直在變) 34 predecessor. next = e; 35 // 最後修改插入位置的上一個節點爲自身,這裏主要是爲了下次遍歷後續元素插入在當前節點的後面,確保這些元素自己的順序 36 predecessor = e; 37 } 38 // 遍歷完全部元素,最後修改下一個節點的上一個元素引用爲遍歷的最後一個元素 39 successor. previous = predecessor; 40 41 // 修改計數器 42 size += numNew; 43 return true; 44 }
1 /** 2 * 刪除第一個匹配的指定元素 3 */ 4 public boolean remove(Object o) { 5 // 遍歷鏈表找到要被刪除的節點 6 if (o==null) { 7 for (Entry<E> e = header .next; e != header; e = e.next ) { 8 if (e.element ==null) { 9 remove(e); 10 return true; 11 } 12 } 13 } else { 14 for (Entry<E> e = header .next; e != header; e = e.next ) { 15 if (o.equals(e.element )) { 16 remove(e); 17 return true; 18 } 19 } 20 } 21 return false; 22 } 23 24 private E remove(Entry<E> e) { 25 if (e == header ) 26 throw new NoSuchElementException(); 27 28 // 被刪除的元素,供返回 29 E result = e. element; 30 // 下面修正先後對該節點的引用 31 // 將該節點的上一個節點的next指向該節點的下一個節點 32 e. previous.next = e.next; 33 // 將該節點的下一個節點的previous指向該節點的上一個節點 34 e. next.previous = e.previous; 35 // 修正該節點自身的先後引用 36 e. next = e.previous = null; 37 // 將自身置空,讓gc能夠儘快回收 38 e. element = null; 39 // 計數器減一 40 size--; 41 modCount++; 42 return result; 43 }
上面對於鏈表增長元素總結了,一句話就是「改變先後的互相指向關係」,刪除也是一樣的道理,因爲節點被刪除,該節點的上一個節點和下一個節點互相拉一下小手就能夠了,注意的是「互相」,不能一廂情願。數組
1 /** 2 * 修改指定位置索引位置的元素 3 */ 4 public E set( int index, E element) { 5 // 查找index位置的節點 6 Entry<E> e = entry(index); 7 // 取出該節點的元素,供返回使用 8 E oldVal = e. element; 9 // 用新元素替換舊元素 10 e. element = element; 11 // 返回舊元素 12 return oldVal; 13 }
1 /** 2 * 查找指定索引位置的元素 3 */ 4 public E get( int index) { 5 return entry(index).element ; 6 } 7 8 /** 9 * 返回指定索引位置的節點 10 */ 11 private Entry<E> entry( int index) { 12 // 越界檢查 13 if (index < 0 || index >= size) 14 throw new IndexOutOfBoundsException( "Index: "+index+ 15 ", Size: "+size ); 16 // 取出頭結點 17 Entry<E> e = header; 18 // size>>1右移一位表明除以2,這裏使用簡單的二分方法,判斷index與list的中間位置的距離 19 if (index < (size >> 1)) { 20 // 若是index距離list中間位置較近,則從頭部向後遍歷(next) 21 for (int i = 0; i <= index; i++) 22 e = e. next; 23 } else { 24 // 若是index距離list中間位置較遠,則從頭部向前遍歷(previous) 25 for (int i = size; i > index; i--) 26 e = e. previous; 27 } 28 return e; 29 }
1 /** 2 * Returns <tt>true</tt> if this list contains the specified element. 3 * More formally, returns <tt>true</tt> if and only if this list contains 4 * at least one element <tt>e</tt> such that 5 * <tt>(o==null ? e==null : o.equals(e))</tt>. 6 * 7 * @param o element whose presence in this list is to be tested 8 * @return <tt> true</tt> if this list contains the specified element 9 */ 10 public boolean contains(Object o) { 11 return indexOf(o) != -1; 12 } 13 14 /** 15 * Returns the index of the first occurrence of the specified element 16 * in this list, or -1 if this list does not contain the element. 17 * More formally, returns the lowest index <tt>i</tt> such that 18 * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>, 19 * or -1 if there is no such index. 20 * 21 * @param o element to search for 22 * @return the index of the first occurrence of the specified element in 23 * this list, or -1 if this list does not contain the element 24 */ 25 public int indexOf(Object o) { 26 int index = 0; 27 if (o==null) { 28 for (Entry e = header .next; e != header; e = e.next ) { 29 if (e.element ==null) 30 return index; 31 index++; 32 } 33 } else { 34 for (Entry e = header .next; e != header; e = e.next ) { 35 if (o.equals(e.element )) 36 return index; 37 index++; 38 } 39 } 40 return -1; 41 } 42 43 /** 44 * Returns the index of the last occurrence of the specified element 45 * in this list, or -1 if this list does not contain the element. 46 * More formally, returns the highest index <tt>i</tt> such that 47 * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>, 48 * or -1 if there is no such index. 49 * 50 * @param o element to search for 51 * @return the index of the last occurrence of the specified element in 52 * this list, or -1 if this list does not contain the element 53 */ 54 public int lastIndexOf(Object o) { 55 int index = size ; 56 if (o==null) { 57 for (Entry e = header .previous; e != header; e = e.previous ) { 58 index--; 59 if (e.element ==null) 60 return index; 61 } 62 } else { 63 for (Entry e = header .previous; e != header; e = e.previous ) { 64 index--; 65 if (o.equals(e.element )) 66 return index; 67 } 68 } 69 return -1; 70 }
1 /** 2 * Returns the number of elements in this list. 3 * 4 * @return the number of elements in this list 5 */ 6 public int size() { 7 return size ; 8 } 9 10 /** 11 * {@inheritDoc} 12 * 13 * <p>This implementation returns <tt>size() == 0 </tt>. 14 */ 15 public boolean isEmpty() { 16 return size() == 0; 17 }
1 /** 2 * Adds the specified element as the tail (last element) of this list. 3 * 4 * @param e the element to add 5 * @return <tt> true</tt> (as specified by {@link Queue#offer}) 6 * @since 1.5 7 */ 8 public boolean offer(E e) { 9 return add(e); 10 } 11 12 /** 13 * Retrieves and removes the head (first element) of this list 14 * @return the head of this list, or <tt>null </tt> if this list is empty 15 * @since 1.5 16 */ 17 public E poll() { 18 if (size ==0) 19 return null; 20 return removeFirst(); 21 } 22 23 /** 24 * Removes and returns the first element from this list. 25 * 26 * @return the first element from this list 27 * @throws NoSuchElementException if this list is empty 28 */ 29 public E removeFirst() { 30 return remove(header .next); 31 } 32 33 /** 34 * Retrieves, but does not remove, the head (first element) of this list. 35 * @return the head of this list, or <tt>null </tt> if this list is empty 36 * @since 1.5 37 */ 38 public E peek() { 39 if (size ==0) 40 return null; 41 return getFirst(); 42 } 43 44 /** 45 * Returns the first element in this list. 46 * 47 * @return the first element in this list 48 * @throws NoSuchElementException if this list is empty 49 */ 50 public E getFirst() { 51 if (size ==0) 52 throw new NoSuchElementException(); 53 54 return header .next. element; 55 } 56 57 /** 58 * Pushes an element onto the stack represented by this list. In other 59 * words, inserts the element at the front of this list. 60 * 61 * <p>This method is equivalent to {@link #addFirst}. 62 * 63 * @param e the element to push 64 * @since 1.6 65 */ 66 public void push(E e) { 67 addFirst(e); 68 } 69 70 /** 71 * Inserts the specified element at the beginning of this list. 72 * 73 * @param e the element to add 74 */ 75 public void addFirst(E e) { 76 addBefore(e, header.next ); 77 }