LinkedList源碼__java

來源:互聯網
上載者:User

LinkedList由雙向鏈表實現,實現了List、Deque、Serializable、Cloneable介面,能被複製和執行個體化。

public class LinkedList<E>        extends AbstractSequentialList<E>        implements List<E>, Deque<E>, Cloneable, java.io.Serializable

1.儲存結構Node

包含了三個欄位,item為資料項目,next為當前節點的下一個節點,prev為當前節點的前一個節點。

    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;        }    }

2.欄位

成員變數也比較簡單,包含了一個頭結點和尾節點,size是鏈表中節點的個數。

    /**     * 鏈表中節點的個數     */    transient int size = 0;    /**     * 頭結點     */    transient Node<E> first;    /**     * 尾節點     */    transient Node<E> last;

3.建構函式

 /**     * 建構函式,什麼也不做     */    public LinkedList() {    }    /**     * 建立一個包含集合中元素的鏈表     */    public LinkedList(Collection<? extends E> c) {        this();        addAll(c);    }

4.添加方法

  添加節點可以使用頭插法也可以使用尾插法,預設使用尾插法添加新節點。

  /**     * 在頭結點添加節點     */    private void linkFirst(E e) {        //記錄當前的前端節點        final Node<E> f = first;        //建立一個新節點,next指向前端節點,prev為null        final Node<E> newNode = new Node<>(null, e, f);        //將newNode設為前端節點        first = newNode;        //如果原來的頭結點為空白        if (f == null)            //將尾節點設為newNode,由於只有一個節點,newNode既是頭結點又是尾節點            last = newNode;        else            //將原來的頭結點的prev指向新建立的節點            f.prev = newNode;        //更新大小        size++;        modCount++;    }    /**     * 從尾部添加節點     */    void linkLast(E e) {        //先儲存尾節點        final Node<E> l = last;        //建立一個prev指向尾節點,next指向空的節點        final Node<E> newNode = new Node<>(l, e, null);        //將新節點設為尾節點        last = newNode;        //如果原來的鏈表是空鏈表        if (l == null)            //新節點也是頭結點            first = newNode;        else            //將原來尾節點的next指向新節點            l.next = newNode;        size++;        modCount++;    }    /**     * 在succ之前插入非空節點     */    void linkBefore(E e, Node<E> succ) {        // 擷取succ的前一個節點        final Node<E> pred = succ.prev;        //建立新節點,prev指向succ的前一個節點,next指向succ        final Node<E> newNode = new Node<>(pred, e, succ);        //將succ的prev執行新建立的節點        succ.prev = newNode;        //如果succ是頭結點        if (pred == null)            //將新建立的節點設為頭結點            first = newNode;        else            //更新pred的next,將它指向新建立的節點            pred.next = newNode;        size++;        modCount++;    } /**     * 在首部添加節點     */    public void addFirst(E e) {        linkFirst(e);    }    /**     * 在尾部添加節點     */    public void addLast(E e) {        linkLast(e);    }/**     * 添加節點,預設在尾部添加     */    public boolean add(E e) {        linkLast(e);        return true;    } /**     * 將集合中的元素添加到鏈表     */    public boolean addAll(Collection<? extends E> c) {        return addAll(size, c);    }    /**     * 在指定的位置,將集合中的元素添加到鏈表     */    public boolean addAll(int index, Collection<? extends E> c) {        //檢查位置是否合法        checkPositionIndex(index);        //將集合轉為數組        Object[] a = c.toArray();        //集合的大小        int numNew = a.length;        if (numNew == 0)            return false;        //pred要插入位置的前一個節點,succ為需要插入節點的位置        Node<E> pred, succ;        //如果要插入位置等於鏈表的大小,說明需要在尾節點後面添加        if (index == size) {            succ = null;            //pred指向要插入位置的前一個節點也就是尾節點            pred = last;        } else {//如果不在尾部添加            //尋找index位置的節點            succ = node(index);            //index位置的前一個節點            pred = succ.prev;        }        //遍曆數組        for (Object o : a) {            //            @SuppressWarnings("unchecked") E e = (E) o;            //建立新節點,prev指向pred            Node<E> newNode = new Node<>(pred, e, null);            // 如果要插入位置的前一個節點是空,說明要插入位置是頭結點            if (pred == null)                first = newNode;            else                //將要插入位置的前一個節點的next指向新建立的節點                pred.next = newNode;            //將pred指向當前節點            pred = newNode;        }        //如果要插入位置為null        if (succ == null) {            //將新插入的最後一個節點設為尾節點            last = pred;        } else {            //將新插入的最後一個節點的next指向原鏈表要插入位置的節點            pred.next = succ;            succ.prev = pred;        }        size += numNew;        modCount++;        return true;    } /**     * 在指定位置添加節點     */    public void add(int index, E element) {        checkPositionIndex(index);        if (index == size)            //在尾部添加            linkLast(element);        else            //找到index處的節點,然後在該節點前添加新節點            linkBefore(element, node(index));    }

5.擷取和尋找

(1)LinkedList提供了正向尋找和反向尋找兩種方法,需要對整個鏈表進行遍曆,如果找到返回對象在鏈表中的位置。

(2)如果根據索引尋找,LinkedList會根據鏈表中節點的個數/2算出中間位置的索引,然後比較,如果大於中間位置的索引,從中間索引之後開始尋找,否則反之。

 /**     * 擷取頭結點     */    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;    }/**     * 是否包含對象o     */    public boolean contains(Object o) {        return indexOf(o) != -1;    } /**     * 根據位置擷取節點     */    public E get(int index) {        //檢查位置是否合法        checkElementIndex(index);        return node(index).item;    }    /**     * 根據位置找到相應的節點     */    Node<E> node(int index) {        // assert isElementIndex(index);        // 計算鏈表的中心節點,如果index在中部之前,從頭結點向後數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;        }    }    // Search Operations    /**     * 正向尋找節點,如果找到返回節點的位置     */    public int indexOf(Object o) {        int index = 0;        if (o == null) {            for (Node<E> x = first; x != null; x = x.next) {                if (x.item == null)                    return index;                index++;            }        } else {            for (Node<E> x = first; x != null; x = x.next) {                if (o.equals(x.item))                    return index;                index++;            }        }        return -1;    }    /**     * 反向尋找節點     */    public int lastIndexOf(Object o) {        int index = size;        if (o == null) {            for (Node<E> x = last; x != null; x = x.prev) {                index--;                if (x.item == null)                    return index;            }        } else {            for (Node<E> x = last; x != null; x = x.prev) {                index--;                if (o.equals(x.item))                    return index;            }        }        return -1;    }

6.刪除

(1)從remove方法中可以看出如果刪除的節點是null的話是單獨進行處理的,LinkedList是允許null值插入的。

(2)刪除節點時同樣需要遍曆鏈表,尋找要刪除的節點,然後刪除。

    /**     * 刪除節點     */    public boolean remove(Object o) {        //如果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;    }  /**     * 清空鏈表     */    public void clear() {        //遍曆節點        for (Node<E> x = first; x != null; ) {            //刪除每一個節點            Node<E> next = x.next;            x.item = null;            x.next = null;            x.prev = null;            x = next;        }        first = last = null;        size = 0;        modCount++;    }  /**     * 刪除節點     */    public E remove(int index) {        checkElementIndex(index);        return unlink(node(index));    }

7.更新

    /**     * 更新節點的值     */    public E set(int index, E element) {        checkElementIndex(index);        //根據位置找到該節點        Node<E> x = node(index);        E oldVal = x.item;        //更新節點的值        x.item = element;        return oldVal;    }

8.Queue操作和Dequeue操作

由於LinkedList實現了Queue介面,可以使用LinkedList類比隊列和雙端隊列,因此提供了一些隊列的操作方法。

 /**     * 返回頭結點的資料,如果此隊列為空白,則返回 null     */    public E peek() {        final Node<E> f = first;        return (f == null) ? null : f.item;    }    /**     * 擷取但不移除此隊列的頭;如果此隊列為空白,則拋出NoSuchElementException異常     */    public E element() {        return getFirst();    }    /**     * 擷取並移除此隊列的頭,如果此隊列為空白,則返回 null     * @return     */    public E poll() {        final Node<E> f = first;        return (f == null) ? null : unlinkFirst(f);    }    /**     * 擷取並移除此隊列的頭,如果此隊列為空白,則拋出NoSuchElementException異常     * @return     */    public E remove() {        return removeFirst();    }    /**     * 將指定的元素插入隊列,從尾部插入     * @param e     * @return     */    public boolean offer(E e) {        return add(e);    }

雙端隊列操作:

  /**     * 將指定的元素插入隊列,從頭部插入     * @param e     * @return     */    public boolean offerFirst(E e) {        addFirst(e);        return true;    }    /**     * 將指定的元素插入此雙端隊列的末尾     */    public boolean offerLast(E e) {        addLast(e);        return true;    }    /**     * 擷取但不移除此雙端隊列的第一個節點;如果此雙端隊列為空白,則返回 null     */    public E peekFirst() {        final Node<E> f = first;        return (f == null) ? null : f.item;    }    /**     * 擷取但不移除雙端隊列的最後一個節點,如果為空白返回null     */    public E peekLast() {        final Node<E> l = last;        return (l == null) ? null : l.item;    }    /**     * 擷取並移除隊列的第一個節點     * @since 1.6     */    public E pollFirst() {        final Node<E> f = first;        return (f == null) ? null : unlinkFirst(f);    }    /**     * 擷取並移除隊列的最後一個節點     */    public E pollLast() {        final Node<E> l = last;        return (l == null) ? null : unlinkLast(l);    }    /**     * 隊列中添加節點,從頭部添加     */    public void push(E e) {        addFirst(e);    }    /**     * 彈出/刪除節點,從頭部刪除     */    public E pop() {        return removeFirst();    }    /**     * 從此雙端隊列移除第一次出現的指定元素,如果列表中不包含次元素,則沒有任何改變     */    public boolean removeFirstOccurrence(Object o) {        return remove(o);    }    /**     * 從此雙端隊列移除最後一次出現的指定元素,如果列表中不包含次元素,則沒有任何改變     */    public boolean removeLastOccurrence(Object o) {        if (o == null) {            for (Node<E> x = last; x != null; x = x.prev) {                if (x.item == null) {                    unlink(x);                    return true;                }            }        } else {            for (Node<E> x = last; x != null; x = x.prev) {                if (o.equals(x.item)) {                    unlink(x);                    return true;                }            }        }        return false;    }

9.其他方法

  private boolean isElementIndex(int index) {        return index >= 0 && index < size;    }    /**     * 檢驗位置是否合法(大於等於0小於等於節點的個數)     */    private boolean isPositionIndex(int index) {        return index >= 0 && index <= size;    }    /**     * 錯誤資訊     */    private String outOfBoundsMsg(int index) {        return "Index: "+index+", Size: "+size;    }    /**     * 檢查索引是否合法     * @param index     */    private void checkElementIndex(int index) {        if (!isElementIndex(index))            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));    }    /**     * 檢查位置是否合法     * @param index     */    private void checkPositionIndex(int index) {        if (!isPositionIndex(index))            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));    }    /**     * 鏈表的大小     */    public int size() {        return size;    }    /**     * 複製方法     */    public Object clone() {        LinkedList<E> clone = superClone();        // Put clone into "virgin" state        clone.first = clone.last = null;        clone.size = 0;        clone.modCount = 0;        // Initialize clone with our elements        for (Node<E> x = first; x != null; x = x.next)            clone.add(x.item);        return clone;    }    /**     * 轉為數組     */    public Object[] toArray() {        //建立數組        Object[] result = new Object[size];        int i = 0;        //遍曆鏈表        for (Node<E> x = first; x != null; x = x.next)            result[i++] = x.item;        return result;    }    /**     * 轉為數組     */    @SuppressWarnings("unchecked")    public <T> T[] toArray(T[] a) {        if (a.length < size)            a = (T[])java.lang.reflect.Array.newInstance(                    a.getClass().getComponentType(), size);        int i = 0;        Object[] result = a;        for (Node<E> x = first; x != null; x = x.next)            result[i++] = x.item;        if (a.length > size)            a[size] = null;        return a;    }    private static final long serialVersionUID = 876323262645176354L;    /**     * 序列化     */    private void writeObject(java.io.ObjectOutputStream s)            throws java.io.IOException {        // Write out any hidden serialization magic        s.defaultWriteObject();        // Write out size        s.writeInt(size);        // Write out all elements in the proper order.        for (Node<E> x = first; x != null; x = x.next)            s.writeObject(x.item);    }    /**     * 還原序列化     */    @SuppressWarnings("unchecked")    private void readObject(java.io.ObjectInputStream s)            throws java.io.IOException, ClassNotFoundException {        // Read in any hidden serialization magic        s.defaultReadObject();        // Read in size        int size = s.readInt();        // Read in all elements in the proper order.        for (int i = 0; i < size; i++)            linkLast((E)s.readObject());    }

總結:

(1)LinkedList基於雙向鏈表實現,插入刪除效率高,同時也可以作為棧、隊列、雙端隊列來使用。

(2)LinkedList是非安全執行緒的。

(3)LinkedList中的元素可以為null。

(4)實現了Serializable、Cloneable介面,能被複製和執行個體化。

參考: jdk1.8.0_45源碼解讀——LinkedList的實現
【Java集合源碼剖析】LinkedList源碼剖析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.