集合(二)List,ArrayList,LinekList,Vector

ArrayList,LinkedList,Vector

List,Queue中的元素有序可重複

ArrayList,LinekList,Vector 均爲可伸縮數組,既可以動態改變長度的數組。

1.ArrayList

數組實現,查詢速度快,插入慢,非線程安全。ArrayList底層維護了一個Object[]用於存儲對象,默認數組的長度是10。可以通過new ArrayList(20)顯式的指定用於存儲對象的數組的長度。

它繼承於AbstractList,實現了List, RandomAccess(標記接口,無任何方法。快速隨機訪問存儲元素), Cloneable(克隆), Serializable(序列化,hessian協議傳輸)接口。

1.添加元素時,ensureCapacityInternal()方法會計算ArrayList的擴容大小。如果需要擴容,擴容的大小爲原來的1.5倍,實際上是使用Arrays.copyOf()方法實現的。

2.刪除元素時,如果不是最後一個元素,最終都會調用System.arraycopy()方法進行數組複製操作

3.modeCount變量的含義:在迭代器初始化時,將modeCount的值賦值給expectedModCount,每一次增刪改,modCount的值都會加1,因此modeCount記錄了ArrayList內發生改變的次數。

迭代獲取下一個元素的時候,會判斷expectedModCount的值與modeCount是否一致,如果不一致,則會報異常

4.transient變量:當序列化對象的時候,如果對象的某個屬性不進行序列化操作,則用transient修飾就可以。

5.elementData是ArrayList集合中保存元素的數組。ArrayList中對於elementData變量就用transient來修飾,爲什麼這個變量不序列化呢?

ArrayList在添加元素時,可能會對elementData數組進行擴容操作,而擴容後的數組可能並沒有全部保存元素。

例如:我們創建了new Object[10]數組對象,但是我們只向其中添加了1個元素,而剩餘的9個位置並沒有添加元素。當我們進行序列化時,並不會只序列化其中一個元素,而是將整個數組進行序列化操作,那些沒有被元素填充的位置也進行了序列化操作,間接的浪費了磁盤的空間,以及程序的性能。所以,ArrayList纔會在elementData屬性前加上transient修飾符。

ArrayList在序列化時會調用writeObject(),直接將elementData寫入ObjectOutputStream;

而反序列化時則調用readObject(),從ObjectInputStream獲取elementData;

Arrays.copyOf()方法的底層調用System.arrayCopy()方法。arrayCopy()方法使用了native關鍵字修飾,調用的是c++的底層函數

2.Vector

隊列實現,並且實現了RandomAccess接口,可以隨機訪問,絕大多數方法都是直接或者間接同步的,是線程安全的ArrayList

Vector和ArrayList都會在內存中開闢一塊連續的空間來存儲,數據存儲是連續的.

3.LinkedList

jdk1.7後, LinkedList變成了直線型鏈表結構,雙向循環鏈表實現的,插入快,查找慢,非線程安全。查找的時候從頭到尾查找,在內存中的地址不一定連續。上一個元素需要記住下一個元素。descendingIterator()函數可以逆序迭代,因此LinkedList可以模擬隊列(list.offer("**")先進先出list.poll())或者堆棧(list.push("***")後進先出list.pop())數據結構

它繼承AbstractSequentialList,實現了List, Deque, Cloneable, Serializable接口。

Deque是一個雙向隊列,既可以先入先出,也可以先入後出(既可以在頭部添加元素,也可以在尾部添加元素)

node(int index)方法至關重要,通過對應角標獲取到對應的集合元素。node()中是根據角標的大小是選擇從前遍歷還是從後遍歷整個集合。也可以間接的說明,LinkedList在隨機獲取元素時性能很低,每次的獲取都得從頭或者從尾遍歷半個集合。

node方法:

//設置對應角標的元素:
public E set(int index, E element) {
    checkElementIndex(index);
    //通過node()方法,獲取到對應角標的元素:
    java.util.LinkedList.Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}
//獲取對應角標所屬於的結點:
java.util.LinkedList.Node<E> node(int index) {
    //位運算:如果位置索引小於列表長度的一半,則從頭開始遍歷;否則,從後開始遍歷;
    if (index < (size >> 1)) {
        java.util.LinkedList.Node<E> x = first;
        //從頭結點開始遍歷:遍歷的長度就是index的長度,獲取對應的index的元素
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        //從集合尾結點遍歷:
        java.util.LinkedList.Node<E> x = last;
        //同樣道理:
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

get()方法:核心還是調用node()方法

參考:https://blog.csdn.net/qq_33642117/article/details/51998866

https://mp.weixin.qq.com/s?__biz=MzI5ODI5NDkxMw==&mid=2247486782&idx=1&sn=67ad4a2e45dfcefc6df5e77d8a94e572&chksm=eca946d0dbdecfc6ee491c600e8899da6f05141ba839f49bae450f08f7118c9a5b8637a55c19&scene=21#wechat_redirect

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