赋值给集合_ArrayList集合源码
ArrayList簡介
ArrayList 是 Java 集合框架中比較常用的數據結構了。ArrayList是可以動態增長和縮減的索引序列,內部封裝了一個動態再分配的Object[]數組
這里我們可以看到ArrayList繼承抽象類AbstractList,實現了 List 接口,同時還實現了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速訪問、復制、序列化的。
主要成員變量
// 底層存儲元素的數組transient Object[] elementData; // ArrayList的實際大小private int size;復制代碼注意:size 才是 elementData數組中實際的元素個數,而 elementData.length 為數組的容量,表示最多可以容納多少個元素。
// ArrayList的默認初始化容量private static final int DEFAULT_CAPACITY = 10;復制代碼ArrayList的默認初始容量大小為 10。
// 記錄對List操作的次數protected transient int modCount = 0;復制代碼這個變量是定義在 AbstractList 中的,主要使用是在 Iterator,目的是防止在List在迭代的過程中被修改。
// 空的Object類型數組private static final Object[] EMPTY_ELEMENTDATA = {};// 空的Object類型數組private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};復制代碼兩個空的數組有什么區別呢?簡單來講就是第一次添加元素(使用add方法)時知道elementData是從空的構造函數還是有參構造函數被初始化的。以便確認下一步的擴容機制。
構造函數
ArrayList類共有三種構造函數:
- 無參構造函數
- 帶有參數為初始容量initialCapacity的構造函數
- 帶有參數為Collection集合的構造函數
1、無參構造函數
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}復制代碼注意:雖然在源碼的注釋中說該構造函數構造一個容量大小為 10 的空的ArrayList,但實際上構造函數只是給 elementData 賦值了一個空的數組(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),在第一次向ArrayList添加元素時容量才擴大至10的。
2、帶有參數為初始容量initialCapacity的構造函數
public ArrayList(int initialCapacity) {// 如果initalCapacity大于0,直接創建一個長度Object數組賦值為elementData;if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];}// 如果initalCapcity等于0,直接將空數組EMPETY_ELEMENTDATA復制給elementDataelse if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;}// 如果initalCapcity小于于0,則拋出異常IllegalArgumentExceptionelse {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}復制代碼當 initialCapacity 為0時則是把 EMPTY_ELEMENTDATA 賦值給 elementData。 當 initialCapacity 大于零時初始化一個大小為 initialCapacity 的 object 數組并賦值給 elementData。
3、帶有參數為Collection集合的構造函數
public ArrayList(Collection<? extends E> c) {// 將 Collection 轉化為數組并賦值給elementDataelementData = c.toArray();// 把elementData中元素的個數賦值給size并判斷其是否為0if ((size = elementData.length) != 0) {// 如果 size 不為零,則判斷 elementData 的 class 類型是否為 Object[],不是的話則做一次轉換。if (elementData.getClass() != Object[].class)elementData = Arrays.copyOf(elementData, size, Object[].class);} // 如果 size 為零,則把 EMPTY_ELEMENTDATA 賦值給 elementData,相當于new ArrayList(0)。else {this.elementData = EMPTY_ELEMENTDATA;}}復制代碼該構造方法主要就是將Collection集合的實現類轉換為ArrayList。
主要操作方法
add方法(添加單個元素)
public boolean add(E e) {// 先確認ArrayList集合容量大小ensureCapacityInternal(size + 1);// 先給elementData中size位置賦值為e,然后size自增 elementData[size++] = e;return true;}復制代碼 private void ensureCapacityInternal(int minCapacity) {// 如果elementData為默認的空數組,則給minCapacity賦值為初始的默認容量if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}// modCount自增,并確定容量大于數組的長度ensureExplicitCapacity(minCapacity);}復制代碼 private void ensureExplicitCapacity(int minCapacity) {// modCount自增,修改次數加1modCount++;// 如果minCapacity超過了數組長度,則進行擴容if (minCapacity - elementData.length > 0)grow(minCapacity);}復制代碼上述三個函數的調用關系很簡單,也很清楚。
- 在add方法中,每次添加元素到ArrayList中時都會先確認下集合容量大小,然后將size位置的元素賦值為e,size再進行自增。
- 在ensureCapacityInternal方法中先對elementData進行判斷 ,如果elementData為 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 就取 DEFAULT_CAPACITY 和 minCapacity 的最大值也就是10。這就是 EMPTY_ELEMENTDATA 與 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的區別所在。同時也驗證了上面的說法:使用無參構造函數時是在第一次添加元素時初始化容量為10 。
- ensureExplicitCapacity 方法中首先對modCount 自增1,記錄操作次數,然后如果 minCapacity 大于 elementData 的長度,則對集合進行擴容。顯然當第一次添加元素時 elementData 的長度為零。那我們來看看 grow 函數。
默認將list的容量擴容至原來容量的 1.5 倍。但是擴容之后也不一定適用,有可能太小,有可能太大。所以才會有下面兩個 if 判斷。如果1.5倍太小的話,則將增加元素的容量大小賦值給newCapacity,如果1.5倍太大或者我們需要的容量太大,則調用hugeCapacity函數,給newCapacity賦一個合適的值。最后將原數組中的數據復制到大小為 newCapacity 的新數組中,并將新數組賦值給 elementData。
private static int hugeCapacity(int minCapacity) {// 如果minCapacity小于0,就拋出異常if (minCapacity < 0) // overflowthrow new OutOfMemoryError();// 如果此時增加元素后得minCapacity大于數組的最大長度就返回整數最大值,否則返回數組最大值return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}復制代碼add方法(批量添加,在指定位置添加)
public void add(int index, E element) {// 檢查index是否越界rangeCheckForAdd(index);ensureCapacityInternal(size + 1); // Increments modCount!!System.arraycopy(elementData, index, elementData, index + 1, size - index);elementData[index] = element;size++; }public boolean addAll(Collection<? extends E> c) {Object[] a = c.toArray();int numNew = a.length;ensureCapacityInternal(size + numNew); // Increments modCountSystem.arraycopy(a, 0, elementData, size, numNew);size += numNew;return numNew != 0; }public boolean addAll(int index, Collection<? extends E> c) {rangeCheckForAdd(index);Object[] a = c.toArray();int numNew = a.length;ensureCapacityInternal(size + numNew); // Increments modCountint numMoved = size - index;if (numMoved > 0)System.arraycopy(elementData, index, elementData, index + numNew, numMoved);System.arraycopy(a, 0, elementData, index, numNew);size += numNew;return numNew != 0; }復制代碼這三個方法基本思路與上述add方法基本思路是一致,博主這里就不再贅述了。
remove方法
public E remove(int index) {// 檢查index是否越界,如果越界則拋出異常rangeCheck(index);// modCount自增,修改次數加一modCount++;// 獲取elementData在index位置的值E oldValue = elementData(index);// 獲取后移的位置長度int numMoved = size - index - 1;// 如果大于零,則調用System.arraycopy方法完成數組移動if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index, numMoved);// size自減,并將elementData索引值為size的元素引用賦值為空,讓GC對他進行回收elementData[--size] = null; // 返回index位置的值return oldValue; }復制代碼當我們調用 remove(int index) 時,首先會檢查 index 是否合法,然后再判斷要刪除的元素是否位于數組的最后一個位置。如果 index 不是最后一個,就再次調用 System.arraycopy() 方法拷貝數組。說白了就是將從 index + 1 開始向后所有的元素都向前挪一個位置。然后將數組的最后一個位置空,size - 1。如果 index 是最后一個元素那么就直接將數組的最后一個位置空,size - 1即可。
public boolean remove(Object o) {// 如果o為空,則查找數組中為空的索引,并調用fastRemove方法進行刪除,并返回trueif (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}}// 如果o不為空,則查找數組中與該元素相等的索引,并調用fastRemove方法進行刪除,并返回true else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}// 如果list中不存在則返回falsereturn false; }復制代碼下面我們在看fastRemove方法,fastRemove方法相較于remove(int index)方法少了一步對index的判斷,因為remove(Object o)就是在數組中進行查詢一定是合法的,所以在fastRemove中沒必要對index進行判斷。
private void fastRemove(int index) {modCount++;int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // clear to let GC do its work }復制代碼get方法
public E get(int index) {// 檢查index是否合法,是否越界rangeCheck(index);// 利用數組的特點,直接訪問數組中該索引位置上的元素return elementData(index); }復制代碼總結
- ArrayList可以存放null。
- ArrayList本質上就是一個elementData數組。
- ArrayList區別于數組的地方在于能夠自動擴展大小,其中關鍵的方法就是gorw()方法。
- ArrayList中removeAll(collection c)和clear()的區別就是removeAll可以刪除批量指定的元素,而clear是全是刪除集合中的元素。
- ArrayList由于本質是數組,所以它在數據的查詢方面會很快,而在插入刪除這些方面,性能下降很多,有移動很多數據才能達到應有的效果
- ArrayList實現了RandomAccess,所以在遍歷它的時候推薦使用for循環。
原文鏈接:https://juejin.cn/post/6911944296451506190
文章來源:掘金
侵權請聯系刪除
總結
以上是生活随笔為你收集整理的赋值给集合_ArrayList集合源码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cad计算机绘图等级考试,国家CAD等级
- 下一篇: 三种方式在CentOS 7搭建KVM虚拟