LinkedList剖析
第1部分 LinkedList介紹
LinkedList簡介
LinkedList 是一個繼承于AbstractSequentialList的雙向鏈表。它也可以被當作堆棧、隊列或雙端隊列進行操作。
Doubly-linked list implementation of the List and Deque interfaces. Implements all optional list operations, and permits all elements (including null).All of the operations perform as could be expected for a doubly-linked list. Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index.Note that this implementation is not synchronized. If multiple threads access a linked list concurrently, and at least one of the threads modifies the list structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes one or more elements; merely setting the value of an element is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the list. If no such object exists, the list should be "wrapped" using the Collections.synchronizedList method. This is best done at creation time, to prevent accidental unsynchronized access to the list:List list = Collections.synchronizedList(new LinkedList(...));The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the Iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs. 以雙向鏈表實現。鏈表無容量限制,但雙向鏈表本身使用了更多空間,每插入一個元素都要構造一個額外的Node對象,也需要額外的鏈表指針操作。 按下標訪問元素-get(i)、set(i,e) 要悲劇的部分遍歷鏈表將指針移動到位 (如果i>數組大小的一半,會從末尾移起)。 插入、刪除元素時修改前后節點的指針即可,不再需要復制移動。但還是要部分遍歷鏈表的指針才能移動到下標所指的位置。 只有在鏈表兩頭的操作-add()、addFirst()、removeLast()或用iterator()上的remove()倒能省掉指針的移動。 Apache Commons 有個TreeNodeList,里面是棵二叉樹,可以快速移動指針到位。LinkedList構造函數???
/*** Constructs an empty list.*/ public LinkedList() { }/*** Constructs a list containing the elements of the specified* collection, in the order they are returned by the collection's* iterator.** @param c the collection whose elements are to be placed into this list* @throws NullPointerException if the specified collection is null*/ public LinkedList(Collection<? extends E> c) {this();addAll(c); }看一個例子
public class Test {public static void main(String[] args) {List<String> list = new LinkedList<String>();list.add("語文: 1");list.add("數學: 2");list.add("英語: 3");} }結構也相對簡單一些,如下圖所示:
?
第2部分 LinkedList數據結構
java.lang.Object? java.util.AbstractCollection<E>? java.util.AbstractList<E>? java.util.AbstractSequentialList<E>? java.util.LinkedList<E>public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, java.io.Serializable {}成員變量
transient int size = 0;/*** Pointer to first node.* Invariant: (first == null && last == null) ||* (first.prev == null && first.item != null)*/ transient Node<E> first;/*** Pointer to last node.* Invariant: (first == null && last == null) ||* (last.next == null && last.item != null)*/ transient Node<E> last; //每一個元素節點 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;} }第3部分 LinkedList源碼api解析
LinkedList實際上是通過雙向鏈表去實現的。既然是雙向鏈表,那么它的順序訪問會非常高效,而隨機訪問效率比較低。
? ? 既然LinkedList是通過雙向鏈表的,但是它也實現了List接口{也就是說,它實現了get(int location)、remove(int location)等“根據索引值來獲取、刪除節點的函數”}。LinkedList是如何實現List的這些接口的,如何將“雙向鏈表和索引值聯系起來的”?
? ? 實際原理非常簡單,它就是通過一個計數索引值來實現的。例如,當我們調用get(int location)時,首先會比較“location”和“雙向鏈表長度的1/2”;若前者大,則從鏈表頭開始往后查找,直到location位置;否則,從鏈表末尾開始先前查找,直到location位置。
? ?這就是“雙線鏈表和索引值聯系起來”的方法。
3.1 add方法
/*** Appends the specified element to the end of this list.** <p>This method is equivalent to {@link #addLast}.** @param e element to be appended to this list* @return {@code true} (as specified by {@link Collection#add})*/ public boolean add(E e) {linkLast(e);return true; } /*** Links e as last element.*/ void linkLast(E e) {final Node<E> l = last;final Node<E> newNode = new Node<>(l, e, null);last = newNode;if (l == null)first = newNode;elsel.next = newNode;size++;modCount++; }3.2. set和get函數
/*** 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) {checkElementIndex(index);Node<E> x = node(index);E oldVal = x.item;x.item = element;return oldVal; } /*** Returns the element at the specified position in this list.** @param index index of the element to return* @return the element at the specified position in this list* @throws IndexOutOfBoundsException {@inheritDoc}*/ public E get(int index) {checkElementIndex(index);return node(index).item; } private void checkElementIndex(int index) {if (!isElementIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); }/*** Tells if the argument is the index of an existing element.*/ private boolean isElementIndex(int index) {return index >= 0 && index < size; }這兩個函數都調用了node函數,該函數會以O(n/2)的性能去獲取一個節點,具體實現如下所示:
/*** Returns the (non-null) Node at the specified element index.*/ Node<E> node(int index) {// assert isElementIndex(index);if (index < (size >> 1)) {Node<E> x = first;for (int i = 0; i < index; i++)x = x.next;return x;} else {Node<E> x = last;for (int i = size - 1; i > index; i--)x = x.prev;return x;} }就是判斷index是在前半區間還是后半區間,如果在前半區間就從head搜索,而在后半區間就從tail搜索。而不是一直從頭到尾的搜索。如此設計,將節點訪問的復雜度由O(n)變為O(n/2)。
總結:
(01) LinkedList 實際上是通過雙向鏈表去實現的。
??????? 它包含一個非常重要的內部類:Node。Node是雙向鏈表節點所對應的數據結構,它包括的屬性有:當前節點所包含的值,上一個節點,下一個節點。
(02) 從LinkedList的實現方式中可以發現,它不存在LinkedList容量不足的問題。
(03) LinkedList的克隆函數,即是將全部元素克隆到一個新的LinkedList對象中。
(04) 由于LinkedList實現了Deque,而Deque接口定義了在雙端隊列兩端訪問元素的方法。提供插入、移除和檢查元素的方法。每種方法都存在兩種形式:一種形式在操作失敗時拋出異常,另一種形式返回一個特殊值(null 或 false,具體取決于操作)。
總結起來如下表格:
(05) LinkedList可以作為FIFO(先進先出)的隊列,作為FIFO的隊列時,下表的方法等價:
隊列方法 等效方法 add(e) addLast(e) offer(e) offerLast(e) remove() removeFirst() poll() pollFirst() element() getFirst() peek() peekFirst()(06) LinkedList可以作為LIFO(后進先出)的棧,作為LIFO的棧時,下表的方法等價:
棧方法 等效方法 push(e) addFirst(e) pop() removeFirst() peek() peekFirst()第4部分 LinkedList遍歷方式
LinkedList遍歷方式
LinkedList支持多種遍歷方式。建議不要采用隨機訪問的方式去遍歷LinkedList
//不可取 for (int i = 0; i < linkList.size(); i++) {link = linkList.get(i); }一般使用增強的for循環(foreach)
參考:
http://www.cnblogs.com/skywang12345/p/3308807.html
?
總結
以上是生活随笔為你收集整理的LinkedList剖析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [密码学基础][每个信息安全博士生应该知
- 下一篇: [Leetcode][第461题][JA