java集合篇(一)——ArrayList擴容原理
阿新 • • 發佈:2018-12-31
相信大家都對ArrayList相當熟悉了,今天筆者就對ArrayList的原始碼進行解讀,講解一下對ArrayList擴容的基本原理。
雖然大家都有用過,但還是簡單介紹一下吧,ArrayList實現了List的介面,並且實現了序列化,同樣具有collection的方法,add,remove等,時間複雜度都是O(1),對於n個數據則為O(n)。好了,接下來具體看下ArrayList的原始碼(筆者使用的是jdk1.8版本):
/** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer. Any * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA * will be expanded to DEFAULT_CAPACITY when the first element is added. */ transient Object[] elementData; // non-private to simplify nested class access /** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
此處我們可以看到,我們的資料其實是放在ArrayList<E>引用的一個Object陣列,也就是elementData陣列,我們來看看它的add方法:
/** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); }
從add傳進來的元素開始,在ensure方法中進行了size的判斷,首先判斷取最小容量,然後對最小容量和目前資料所需容量最比對,如果最小容量並不滿足,則需要增加大小:
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
這裡新的容量是將就舊的加上後移一位的,也就是二進位制位右移,然後進行判斷,如果新增的比最小容量要小,則賦值為最小容量,如果超過了最大值,則賦值int的最大值,也就是2^31-1,十六進位制數為0x7fffffff,然後呼叫Arrays的copyof方法去將原來陣列的值複製過去。
這裡可以做一個測試demo,由於屬性是非公有的,所以使用了反射的方法去獲取(關於反射的使用可以參考筆者的另一篇文章):
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
TestIntValue value = new TestIntValue();
list.add("ss");
int actualLength = value.getActualLength(list);
int size = list.size();
System.out.println("list此時的容量為:" + actualLength);
System.out.println("list此時的大小為:" + size);
}
/**
* 反射獲取elementData容量大小
* @param mList
* @return
*/
public int getActualLength(ArrayList<String> mList){
Class<?> mClass = mList.getClass();
Field f = null;
int length = 0;
try {
f = mClass.getDeclaredField("elementData");
f.setAccessible(true);
Object[] o = (Object[])f.get(mList);
length = o.length;
} catch (Exception e) {
e.printStackTrace();
}
return length;
}
此時只是增加了一個值,所以此時的大小為1,容量大小為10:
如果我們給他塞入超出10的值呢:
此時容量大小已經變成15了,可見,大小的確是10 + 5來的,所以,他的擴容是大概增加了1.5倍的大小。
以上就是擴容的過程了,謝謝大家~