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