java 學習之四 ArrayList

ArrayList的主要特點

ArrayList比較適合順序添加、隨機訪問的場景
ArrayList允許元素數目爲
ArrayList允許重複數據
       ArrayList是元素有序  (指的是插入和取出順序相同)
        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");
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章