深入理解JAVA集合系列四:ArrayList源碼解讀
在開始本章內容之前,這裏先簡單介紹下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源碼解讀