ArrayList的主要特點
添加元素
有這麼一段代碼:
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
list.add("000");
list.add("111");
}
看下底層會做什麼,進入add方法的源碼來看一下:1 public boolean add(E e) {
2 ensureCapacity(size + 1); // Increments modCount!!
3 elementData[size++] = e;
4 return true;
5 }
先不去管第2行的ensureCapacity方法,這個方法是擴容用的,底層實際上在調用add方法的時候只是給elementData的某個位置添加了一個數據而已,用一張圖表示的話是這樣的:多說一句,我這麼畫圖有一定的誤導性。elementData中存儲的應該是堆內存中元素的引用,而不是實際的元素,這麼畫給人一種感覺就是說elementData數組裏面存放的就是實際的元素,這是不太嚴謹的。不過這麼畫主要是爲了方便起見,只要知道這個問題就好了。
擴容
我們看一下,構造ArrayList的時候,默認的底層數組大小是10: (有的地方看到的寫的是16)
public ArrayList() {
this(10);
}
那麼有一個問題來了,底層數組的大小不夠了怎麼辦?答案就是擴容,這也就是爲什麼一直說ArrayList的底層是基於動態數組實現的原因,動態數組的意思就是指底層的數組大小並不是固定的,而是根據添加的元素大小進行一個判斷,不夠的話就動態擴容,擴容的代碼就在ensureCapacity裏面:public void ensureCapacity(int minCapacity) {
<span> </span> modCount++;
<span> </span>int oldCapacity = elementData.length;
<span> </span>if (minCapacity > oldCapacity) {
<span> </span>Object oldData[] = elementData;
<span> </span>int newCapacity = (oldCapacity * 3)/2 + 1;
<span> </span>if (newCapacity < minCapacity)
<span> </span>newCapacity = minCapacity;
<span> </span>// minCapacity is usually close to size, so this is a win:
<span> </span>elementData = Arrays.copyOf(elementData, newCapacity);
<span> </span>}
}
看到擴容的時候把元素組大小先乘以3,再除以2,最後加1。可能有些人要問爲什麼?我們可以想:
1、如果一次性擴容擴得太大,必然造成內存空間的浪費
2、如果一次性擴容擴得不夠,那麼下一次擴容的操作必然比較快地會到來,這會降低程序運行效率,要知道擴容還是比價耗費性能的一個操作
所以擴容擴多少,是JDK開發人員在時間、空間上做的一個權衡,提供出來的一個比較合理的數值。最後調用到的是Arrays的copyOf方法,將元素組裏面的內容複製到新的數組裏面去:
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
用一張圖來表示就是這樣的:
刪除元素
接着我們看一下刪除的操作。ArrayList支持兩種刪除方式:
1、按照下標刪除
2、按照元素刪除,這會刪除ArrayList中與指定要刪除的元素匹配的第一個元素
對於ArrayList來說,這兩種刪除的方法差不多,都是調用的下面一段代碼:
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
其實做的事情就是兩件:
1、把指定元素後面位置的所有元素,利用System.arraycopy方法整體向前移動一個位置
2、最後一個位置的元素指定爲null,這樣讓gc可以去回收它
比方說有這麼一段代碼:
public static void main(String[] args)
{
List<String> list = new ArrayList<String>();
list.add("111");
list.add("222");
list.add("333");
list.add("444");
list.add("555");
list.add("666");
list.add("777");
list.add("888");
list.remove("333");
}