深入理解系列之JAVA數據結構(2)——LinkedList

1、LinkedList 是一個繼承於AbstractSequentialList的雙向鏈表。它也可以被當作堆棧、隊列或雙端隊列進行操作。
2、LinkedList相對於ArrayList來說,是可以快速添加,刪除元素,ArrayList添加刪除元素的話需移動數組元素,可能還需要考慮到擴容數組長度。
3、 LinkedList 實現 List 接口,能對它進行隊列操作。 LinkedList 實現 Deque接口,即能將LinkedList當作雙端隊列使用。 LinkedList 實現了Cloneable接口,即覆蓋了函數clone(),能克隆。
4、 LinkedList 實現java.io.Serializable接口,這意味着LinkedList支持序列化,能通過序列化去傳輸。
5、 LinkedList 是非同步的。

同樣,首先還是看下linkedList的屬性:

public class LinkedList<E>  
    extends AbstractSequentialList<E>  
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{     
    //當前有多少個節點  
    transient int size = 0;  
    //第一個節點  
    transient Node<E> first;  
    //最後一個節點  
    transient Node<E> last;  

其中使用transient修飾的參數指的是序列化的時候忽略該參數字段,使得該參數只能停留在內存中而不會被持久化在硬盤中或者傳輸到網絡中!

鏈表相對來說就是節點的連接問題,在學數據結構的時候應該都比較熟悉,所以只有兩個問題。

問題一、集合參數構造器的原理?

類似於ArrayList,默認構造函數什麼都沒做,即初始化一個空鏈表,只有在添加數據的時候才new一個新節點;傳遞集合的構造函數也是類似,首先把集合轉變爲一個數組,然後再遍歷數組把各個數據連接起來,看源碼:

public LinkedList(Collection<? extends E> c) {  
    this();  
    addAll(c);  
}  
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;  

    Node<E> pred, succ;  
    if (index == size) {  
        succ = null;  
        pred = last;  
    } else {  
        succ = node(index);  
        pred = succ.prev;  
    }  

    for (Object o : a) {  
        @SuppressWarnings("unchecked") E e = (E) o;  
        Node<E> newNode = new Node<>(pred, e, null);  
        if (pred == null)  
            first = newNode;  
        else  
            pred.next = newNode;  
        pred = newNode;  
    }  

    if (succ == null) {  
        last = pred;  
    } else {  
        pred.next = succ;  
        succ.prev = pred;  
    }  

    size += numNew;  
    modCount++;  
    return true;  
}  

問題二、獲取特定位置的索引是怎麼做的?

由於鏈表是雙向鏈表,類參數維護的有一個節點數量size,所以當索引特定位置的時候,會判斷當前位置是在左側還是右側,然後決定起始索引節點是頭結點還是尾節點

Node<E> node(int 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;  
    }  
}  

其他:

1、同樣,鏈表也會維護一個modCount!所以在使用迭代器的時候同樣會出現ConcurrentModificationException異常!
2、鏈表維護的還有first和last節點,所以獲取第一個節點和最後一個節點很方便;
3、鏈表有方法push其實就是調用addFirst(e)方法,pop調用的就是removeFirst()方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章