這里主要包含ArrayList和LinkedList.
關於Java中的集合內容, 感覺都已經被寫爛了, 我這里主要是做個復習, 再從扒下源代碼, 盡量用最直白的語言把里面的核心內容記錄下來。僅此而已。
首先放一個Collection下的UML圖:(此圖是idea通過diagram功能生成的, 基於JDK7)
一、ArrayList
1, for-each原理:
0、 在編譯的時候編譯器會自動將對for這個關鍵字的使用轉化為對目標的迭代器的使用,這就是foreach循環的原理
1、ArrayList之所以能使用foreach循環遍歷,是因為ArrayList所有的List都是Collection的子接口,而Collection是Iterable的子接口,ArrayList的父類AbstractList正確地實現了Iterable接口的iterator方法。之前我自己寫的ArrayList用foreach循環直接報空指針異常是因為我自己寫的ArrayList並沒有實現Iterable接口
2、任何一個集合,無論是JDK提供的還是自己寫的,只要想使用foreach循環遍歷,就必須正確地實現Iterable接口。
2, 集合中的 fail-fast iterator:
Iterator 是工作在一個獨立的線程中,並且擁有一個 mutex 鎖。 Iterator 被創建之后會建立一個指向原來對象的單鏈索引表,當原來的對象數量發生變化時,這個索引表的內容不會同步改變,所以當索引指針往后移動的時候就找不到要迭代的對象,所以按照 fail-fast 原則 Iterator 會馬上拋出 java.util.ConcurrentModificationException 異常。
當使用foreach遍歷一個list元素時, 因為foreach底層實現是使用iteator中的hasNext, next等, 源碼中next執行時會checkForComdification:
3, ArrayList初始值大小是10, 每次擴容是增加2倍。優缺點是:
//默認的構造函數, 當然還有構造函數是可以指定大小的
public ArrayList() {
this(10);
}
//jdk7中的擴容代碼, 我看了jdk6中是擴容1.5倍的, 這里有點差別
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
ArrayList的優點:
1、ArrayList底層以數組實現,是一種隨機訪問模式,再加上它實現了RandomAccess接口,因此查找也就是get的時候非常快
2、ArrayList在順序添加一個元素的時候非常方便,只是往數組里面添加了一個元素而已
ArrayList的缺點:
1、刪除元素的時候,涉及到一次元素復制,如果要復制的元素很多,那么就會比較耗費性能
2、插入元素的時候,涉及到一次元素復制,如果要復制的元素很多,那么就會比較耗費性能
因此,ArrayList比較適合順序添加、隨機訪問的場景。
4,細節: 為什么ArrayList的elementData是用transient修飾的?
ArrayList實現了Serializable接口,這意味着ArrayList是可以被序列化的,用transient修飾elementData意味着我不希望elementData數組被序列化。這是為什么?因為序列化ArrayList的時候,ArrayList里面的elementData未必是滿的,比方說elementData有10的大小,但是我只用了其中的3個,那么是否有必要序列化整個elementData呢?顯然沒有這個必要,因此ArrayList中重寫了writeObject方法:
private transient Object[] elementData;
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out array length
s.writeInt(elementData.length);
// Write out all elements in the proper order.
for (int i=0; i<size; i++)
s.writeObject(elementData[i]);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
每次序列化的時候調用這個方法,先調用defaultWriteObject()方法序列化ArrayList中的非transient元素,elementData不去序列化它,然后遍歷elementData,只序列化那些有的元素,這樣:
1、加快了序列化的速度
2、減小了序列化之后的文件大小