常用數據結構之ArrayList


前言

    ArrayList想必是廣大Java程序員開發時最常用的數據結構了,但不一定對其原理都有了解,今天我將結合ArrayList的源碼對其進行講解。本文將圍繞ArrayList主要特性(包括適用場景、初始大小、擴容等)、數據存放方式、核心方法實現、其他特性等四個方面進行講解。

一、ArrayList特性

    ArrayList是基於數組的數據結構,與LinkedList相比,更加適合在查詢多、增刪操作少的場景下使用,並且它是非線程安全的,如果並發量比較大的場景,需要改用線程安全的版本或者用JUC包中的CopyOnWriteArrayList。

    它的初始數組大小為10,由下圖所示的成員變量控制:

 

     當新加元素時原數組已經滿了,則會觸發擴容,擴容策略為將原數組長度*1.5,代碼中是用右移位運算實現的,源碼如下所示:

二、數據存放方式

    ArrayList是以數組的方式存放數據的,Object[],如下所示:

三、核心方法實現

    我們看最常用的add方法:

1 public boolean add(E e) {
2         ensureCapacityInternal(size + 1);  // 如果已經滿了,則擴容
3         elementData[size++] = e;
4         return true;
5     }

    下面方法用於判斷是否原數組已經滿了,如果滿了則擴容,不滿且未初始化則初始化長度為10,否則不用變化

1 private void ensureCapacityInternal(int minCapacity) {
2         ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
3     }

    下面方法用於返回數組需要的最小長度。

1 private static int calculateCapacity(Object[] elementData, int minCapacity) {
2         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
3             return Math.max(DEFAULT_CAPACITY, minCapacity);
4         }
5         return minCapacity;
6     }

    下面的方法用於判斷是否需要擴容,如果需要則通過grow方法擴容。

1 private void ensureExplicitCapacity(int minCapacity) {
2         modCount++;
3 
4         // overflow-conscious code
5         if (minCapacity - elementData.length > 0) // 如果需要的最小長度大於當前數組總長度,則走grow擴容
6             grow(minCapacity);
7     }

    下面的grow方法是控制擴容的核心方法:

 1 private void grow(int minCapacity) {
 2         // overflow-conscious code
 3         int oldCapacity = elementData.length;
 4         int newCapacity = oldCapacity + (oldCapacity >> 1);
 5         if (newCapacity - minCapacity < 0) // 如果擴容之后的長度小於需要的最小長度,則取最小長度為待擴容長度,這種情況一般不會出現
 6             newCapacity = minCapacity;
 7         if (newCapacity - MAX_ARRAY_SIZE > 0)
 8             newCapacity = hugeCapacity(minCapacity);
 9         // minCapacity is usually close to size, so this is a win:
10         elementData = Arrays.copyOf(elementData, newCapacity); // 創建新數組,將老數組的數據復制過去
11     }

    注意復制數組時,用的Arrays.copyOf方法,該方法最終引用的是System.arraycopy這個native方法實現的數組復制。其他方法更加簡單,此處就不一一粘貼源碼解讀了。

四、其他特性

1、關於modCount

    在看ArrayList源碼的時候,會發現有一個變量是modCount,在增刪改的方法中均涉及到對它的++操作。modCount屬性是在AbstractList中定義出來的:

 

    可以把它理解成每個ArrayList的一個改動的版本號,只要ArrayList有改動,這個版本號就會+1。在通過迭代器對ArrayList里面的元素進行遍歷操作時,每次都會比較一下這個版本號是否有變化,如果檢測到預期之外的變化,就會拋出常見的那個異常-ConcurrentModificationException:

 

    ArrayList東西不多,實現也相對比較簡單,面試時現在一般會跟Vector或者LinkedList進行比對或者作為多線程的一個引子來問,本文就先到這里,下一期對LinkedList進行解讀。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM