ArrayList
ArrayList
實現於 List
、RandomAccess
接口。可以插入空數據,也支持隨機訪問。
ArrayList
相當於動態數據,其中最重要的兩個屬性分別是: elementData
數組,以及 size
大小。 在調用 add()
方法的時候:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
- 首先進行擴容校驗。
- 將插入的值放到尾部,並將 size + 1 。
如果是調用 add(index,e)
在指定位置添加的話:
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++;
}
- 也是首先擴容校驗。
- 接着對數據進行復制,目的是把 index 位置空出來放本次插入的數據,並將後面的數據向後移動一個位置。
其實擴容最終調用的代碼:
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
的主要消耗是數組擴容以及在指定位置添加數據,在日常使用時最好是指定大小,儘量減少擴容。更要減少在指定位置插入數據的操作。
序列化
由於 ArrayList 是基於動態數組實現的,所以並不是所有的空間都被使用。因此使用了 transient
修飾,可以防止被自動序列化。
transient Object[] elementData;
因此 ArrayList 自定義了序列化與反序列化:
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();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
當對象中自定義了 writeObject 和 readObject 方法時,JVM 會調用這兩個自定義方法來實現序列化與反序列化。
從實現中可以看出 ArrayList 只序列化了被使用的數據。
Vector
Vector
也是實現於 List
接口,底層數據結構和 ArrayList
類似,也是一個動態數組存放數據。不過是在 add()
方法的時候使用 synchronized
進行同步寫數據,但是開銷較大,所以 Vector
是一個同步容器並不是一個併發容器。
以下是 add()
方法:
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
以及指定位置插入數據:
public void add(int index, E element) {
insertElementAt(element, index);
}
public synchronized void insertElementAt(E obj, int index) {
modCount++;
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}