Java中的集合之ArrayList,Vector和Stack
這三個集合型別,其底層都是陣列實現的。討論集合關注的問題:
- 底層資料結構
- 增刪改查方式
- 初始容量,擴容方式,擴容時機
- 執行緒安全與否
- 是否允許空,是否允許重複,是否有序
ArrayList
ArrayList是實現List介面的動態陣列。實現了所有可選列表操作,並允許包括 null 在內的所有元素。除了實現 List 介面外,此類還提供一些方法來操作內部用來儲存列表的陣列的大小。
每個陣列都有一個容量,由一個數組維護,初始的容量為10(在第一個元素新增進來時擴容),也可以在初始化new操作時給定。ArrayList繼承自AbstractList,實現了List,RandomAccess,Cloneable,.Serializable介面。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
每次新增新的元素時:先判斷合法性,會判斷陣列的長度,當超過當前的容量大小時,則會擴容一個新的1.5倍的陣列,使用Arrays.copyOf將物件複製到新陣列中。內部陣列elementData使用transient修飾,即進行序列化時,只複製有值的內容,可以節省空間。還可以插入到制定的位置下,同樣操作,使用System.copyOf操作,Arrays.copyOf最終呼叫的也是該方法。刪除也會拷貝,清空則是置為null。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
//擴容
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的所有方法都沒有加鎖或同步控制,所以是非線性安全的。可以在建立時,使用Collection.synchronizedList使其對外執行緒同步。內建一個modCount進行控制,對內部陣列進行了增刪改動後,該值會++,後續使用迭代器時判斷與expectedModCount不相同則丟擲異常。“Fast-Fail機制”
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 size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// 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();
}
}
Vector
Vector是實現了List的動態可變集合。Vector同樣繼承自AbstractList,實現了List,RandomAccess,Cloneable,.Serializable介面。基本和ArrayList類似,內部也是一個物件陣列elementData維護,不過沒有transient,意味著序列化全部內容。
初始化,預設直接為10,不需要第一次載入。另外,除了初始化大小外,還可以制定增長的寬度。
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
/**
* Constructs an empty vector so that its internal data array
* has size {@code 10} and its standard capacity increment is
* zero.
*/
public Vector() {
this(10);
}
新增元素時,同樣會對modCount++,同時判斷當前的大小,加1時超過容量則進行擴容。如果設定了擴容增量大小則按該值,否則就擴容成原來的兩倍,同樣是使用了System.copyOf進行操作。擴容完畢後進行容量的更新。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
Vector中所有對資料進行修改的方法都加了synchronized修飾,來保證執行緒安全性。
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
Stack
Stack是一種棧結構,在Java中其只繼承自Vector。棧很常見,就是一種先進後出,或者說後進先出的資料結構。Stack使用了Vector中的陣列維護資料結構,在Vector外只實現了pop,push,peek,empty,search5個另外的方法。
同樣地,這些方法基本都是使用synchronized修飾,因此Stack是線性安全的。
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
/* @return the object at the top of this stack (the last item
* of the <tt>Vector</tt> object).
* @throws EmptyStackException if this stack is empty.
*/
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
小結:
總的來說,ArrayList實現了RandomAccess,所以隨機訪問快速;但是在進行插入或刪除資料的時候,要使用System.copyOf進行大量的資料拷貝,浪費資源。且ArrayList線性不安全。
Vector同樣可以隨機讀取,和ArrayList類似在增刪資料時涉及大量拷貝,但是優點是線性安全的。
Stack用於一些特定的資料結構需求中。