1. 程式人生 > >深入理解JAVA集合系列四:ArrayList源碼解讀

深入理解JAVA集合系列四:ArrayList源碼解讀

結束 了解 數組下標 size new 數組元素 開始 ini rem

在開始本章內容之前,這裏先簡單介紹下List的相關內容。

List的簡單介紹

有序的collection,用戶可以對列表中每個元素的插入位置進行精確的控制。用戶可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。列表通常允許重復的元素,且允許null元素的存放。

ArrayList的簡單介紹

JDK中這樣定義ArrayList:List接口的大小可變數據的實現。

主要有以下特點:

1、有序

2、線程不安全

3、元素可以重復

4、可以存放null值

顧名思義,取名ArrayList是因為其內部是用一個大小可變的數組存儲元素,也就是說底層是一個動態數組。

add方法解讀

List<String> li = new ArrayList<String>();
li.add("33");

先看這個構造函數:

 public ArrayList() {
    this(10);
    }
技術分享
1  public ArrayList(int initialCapacity) {
2     super();
3         if (initialCapacity < 0)
4             throw new IllegalArgumentException("Illegal Capacity: "+
5                                                initialCapacity);
6     this.elementData = new Object[initialCapacity];
7     }
技術分享

這裏先定義默認的初始容量為10,然後構建了一個容量為10的數組elementData;這個elementData數組就是ArrayList底層存放元素的數組,且元素是Object類型。該數組的源碼定義也了解下:

private transient Object[] elementData;

再來看ADD方法:

技術分享
 1 public void add(int index, E element) {
 2     if (index > size || index < 0)
 3         throw new IndexOutOfBoundsException(
 4         "Index: "+index+", Size: "+size);
 5 
 6     ensureCapacity(size+1);  // Increments modCount!!
 7     System.arraycopy(elementData, index, elementData, index + 1,
 8              size - index);
 9     elementData[index] = element;
10     size++;
11     }
技術分享

1、第6行的擴容部分後面再解釋,其余的比較簡單,我們直接看第7行。調用的是System.arraycopy的方法。

2、第一次看到這個方法有點搞不懂,先來解釋下:System.arraycopy(src, srcPos, dest, destPos, length);

第一個參數表示的是要被復制的數組;第二個參數表示數組中從第幾個元素開始復制。

第三個參數表示復制到哪個數組;第四個參數表示復制到那個數組從哪個元素開始;第五個參數表示要復制的長度

3、解釋完這個方法就很清楚了,add方法就是將數組中待添加元素的位置後面所有的元素(包括該位置),通過copy的復制方法,全部後移一個位置。然後將待添加的元素放到指 定的位置上。當然這個數組可能是原數組,也可能是經過第6行代碼擴容之後新的數組。

擴容

擴容,動態擴容,也就是前面所提到的ArrayList的底層是由一個動態變化的數組實現的。

技術分享
 1 public void ensureCapacity(int minCapacity) {
 2     modCount++;
 3     int oldCapacity = elementData.length;
 4     if (minCapacity > oldCapacity) {
 5         Object oldData[] = elementData;
 6         int newCapacity = (oldCapacity * 3)/2 + 1;
 7             if (newCapacity < minCapacity)
 8         newCapacity = minCapacity;
 9             // minCapacity is usually close to size, so this is a win:
10             elementData = Arrays.copyOf(elementData, newCapacity);
11     }
12     }
技術分享

1、我們看擴容的算法,也就是在舊容量的基礎上,擴大1.5倍再加上1。至於為什麽是這樣一個算法,我也沒明白。但是JDK開發人員肯定是在這方面做過思考的:不能一次性擴容太大,會造成內存空間的浪費;也不能擴容得不夠,否則下一次擴容的操作會很快到來。我猜想應該是基於這樣的考慮才設計了這樣一個算法。

2、擴容結束後,就是將舊數組裏面的元素全部復制到新的數組裏。並且新數組還是通過elementData變量來引用。

remove方法解讀

我們先來看下該方法的源碼:

技術分享
 1  public E remove(int index) {
 2     RangeCheck(index);
 3 
 4     modCount++;
 5     E oldValue = (E) elementData[index];
 6 
 7     int numMoved = size - index - 1;
 8     if (numMoved > 0)
 9         System.arraycopy(elementData, index+1, elementData, index,
10                  numMoved);
11     elementData[--size] = null; // Let gc do its work
12 
13     return oldValue;
14     }
技術分享

其實這個remove的方法處理的過程也比較簡單:

1、第2行代碼是用來檢測數組下標是否越界。

2、然後取出該位置上的數組元素。

3、通過arryCopy方法將待刪除元素之後的所有元素往前一個位置復制。(註意,這個動作是先復制,然後粘貼)。

4、這個時候最後一個位置上的元素就是多余的,所以才有了第11行的處理,將其置為null。

ArrayList的優缺點

1、底層是由數組實現的,便於隨機訪問。

2、順序添加元素性能比較高,相當於每次在數組最後添加一個元素、

3、插入元素、刪除元素的時候,會涉及到元素的復制,即調用arrayCopy方法,如果復制的元素比較多,則這樣就比較消耗性能。

總結來說:ArrayList的優勢是隨機訪問、順序添加。

深入理解JAVA集合系列四:ArrayList源碼解讀