您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關java集合中ArrayList源碼解析是怎樣的,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
ArrayList實際就是個數組結構,如圖
index:數組下標
elementData:數組本身
其他基本概念:
/** * Default initial capacity. * 數組初始大小 */ private static final int DEFAULT_CAPACITY = 10;
/** * The size of the ArrayList (the number of elements it contains). * 當前數組大小 * @serial */ private int size;
//表示當前數組被修改的版本次數 protected transient int modCount = 0;
類注釋:
1、可以自動擴容
2、允許put null
3、size、set、put、add、get時間復雜度O(1)
4、非線程安全(作為共享變量時存在),必要時可以使用線程安全的SynchronizedList(性能低)
public boolean add(E e) { synchronized (mutex) {// synchronized 是一種輕量鎖,mutex 表示一個當前 SynchronizedList return c.add(e); } }
5、增強for循環或迭代時,若數組大小改變,則拋出異常
一、初始化:
//1、指定大小初始化 public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } } //2、無參初始化 數組大小為空//初始化時數組大小不是默認的10,第一次add時擴容為10public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //3、指定初始數據初始化public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } }
二、新增、擴容、刪除
新增:
public boolean add(E e) { //確保數組大小是否足夠,不夠執行擴容,size 為當前數組的大小 ensureCapacityInternal(size + 1); // Increments modCount!! //直接賦值,線程不安全的 elementData[size++] = e; 對null無校驗,因此可以存入null值 return true; }
擴容:
private void ensureCapacityInternal(int minCapacity) { //如果初始化數組大小時,有給定初始值,以給定的大小為準,不走 if 邏輯 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } //確保容積足夠 ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { //記錄數組被修改 modCount++; // 如果我們期望的最小容量大于目前數組的長度,那么就擴容 if (minCapacity - elementData.length > 0) grow(minCapacity); } //擴容,并把現有數據拷貝到新的數組里面去 private void grow(int minCapacity) { int oldCapacity = elementData.length; // oldCapacity >> 1 是把 oldCapacity 除以 2 的意思 這里注意:ArrayList擴容是1+0.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); // 如果擴容后的值 < 我們的期望值,擴容后的值就等于我們的期望值 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果擴容后的值 > jvm 所能分配的數組的最大值,那么就用 Integer 的最大值 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); 擴容時源碼中對上下數組大小做了校驗,有很清晰的數組大小溢出意識,平時開發時應借鑒 // 通過復制進行擴容 elementData = Arrays.copyOf(elementData, newCapacity); }
擴容動態圖:
刪除:
public boolean remove(Object o) { // 如果要刪除的值是 null,找到第一個值是 null 的刪除 if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { 可以刪除null值 fastRemove(index); return true; } } else { // 如果要刪除的值不為 null,找到第一個和要刪除的值相等的刪除 for (int index = 0; index < size; index++) // 這里是根據 equals 來判斷值相等的,相等后再根據索引位置進行刪除 if (o.equals(elementData[index])) { //找到需要刪除的索引 fastRemove(index); return true; } } return false; } //根據索引位置進行元素刪除private void fastRemove(int index) { // 記錄數組的結構要發生變動了 modCount++; // numMoved 表示刪除 index 位置的元素后,需要從 index 后移動多少個元素到前面去 // 減 1 的原因,是因為 size 從 1 開始算起,index 從 0開始算起 int numMoved = size - index - 1; if (numMoved > 0) // 從 index +1 位置開始被拷貝,拷貝的起始位置是 index,長度是 numMoved System.arraycopy(elementData, index+1, elementData, index, numMoved); //數組最后一個位置賦值 null,幫助 GC elementData[--size] = null; }
三、迭代器
implement java.util.Iterator
迭代器的一些參數:
int cursor;// 迭代過程中,下一個元素的位置,默認從 0 開始。
int lastRet = -1; // 新增場景:表示上一次迭代過程中,索引的位置;刪除場景:為 -1。
int expectedModCount = modCount;// expectedModCount 表示迭代過程中,期望的版本號;modCount 表示數組實際的版本號。
迭代器源碼分析:
//是否存在下一個可迭代的值 public boolean hasNext() { return cursor != size;//cursor 表示下一個元素的位置,size 表示實際大小,如果兩者相等,說明已經沒有元素可以迭代了,如果不等,說明還可以迭代 } //下一個可迭代的值 public E next() { //迭代過程中,判斷版本號有無被修改,有被修改,拋 ConcurrentModificationException 異常 checkForComodification(); //本次迭代過程中,元素的索引位置 int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); // 下一次迭代時,元素的位置,為下一次迭代做準備 cursor = i + 1; // 返回元素值 return (E) elementData[lastRet = i]; } // 版本號比較 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } public void remove() { // 如果上一次操作時,數組的位置已經小于 0 了,說明數組已經被刪除完了 if (lastRet < 0) throw new IllegalStateException(); //迭代過程中,判斷版本號有無被修改,有被修改,拋 ConcurrentModificationException 異常 checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; // -1 表示元素已經被刪除,這里也防止重復刪除lastRet = -1; // 刪除元素時 modCount 的值已經發生變化,在此賦值給 expectedModCount // 這樣下次迭代時,兩者的值是一致的了 expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
看完上述內容,你們對java集合中ArrayList源碼解析是怎樣的有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。