Java集合--ArrayList源碼
ArrayList是Java的動態數組集合,主要用於快速讀取數組元素,且在讀多寫少的情況下具有較好的表現;相比於LinedList,更節省空間,因為LinkedList的元素還要多存儲前後繼節點的指針,相較於ArrayList只存儲元素本身有一定的差距,但是ArrayList在使用不當的時候也容易浪費內存,例如大量數據不斷寫入ArrayList時,會不斷分配新的內存,並復制原有數據到新的內存中。
1 /** 2 * The array buffer into which the elements of the ArrayList are stored. 3 * The capacity of the ArrayList is the length of this array buffer. Any4 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 5 * will be expanded to DEFAULT_CAPACITY when the first element is added. 6 */ 7 transient Object[] elementData; // non-private to simplify nested class access
elementData數組是ArrayList最重要的屬性之一,用於存儲實際的數據;elementData為什麽會被定義為transient關鍵字修飾呢?因為ArrayList多數情況下會出現空余數組空間,如果序列化全部的數組,那麽會造成時間和空間的額外消耗;ArrayList提供了自己的writeObject方法和readObject方法,只序列化了實際存儲的元素。
/** * The size of the ArrayList (the number of elements it contains). * * @serial */ private int size;
size是elementData數組中實際存儲的元素的個數,區別於elementData.length,在ArrayList中,elementData數組的長度是ArrayList的實際開辟空間的長度
/** * This helper method split out from add(E) to keep method * bytecode size under 35 (the -XX:MaxInlineSize default value), * which helps when add(E) is called in a C1-compiled loop.*/ private void add(E e, Object[] elementData, int s) { if (s == elementData.length) elementData = grow(); //grow()方法擴充開辟新空間,並復制數據到新空間 elementData[s] = e; size = s + 1; } /** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { modCount++; add(e, elementData, size); return true; }
從add(E e)方法中可以分析出,數組中當前元素的個數等於數組的長度時,會增長當前數組的長度;
/** * 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 * @throws OutOfMemoryError if minCapacity is less than zero */ private Object[] grow(int minCapacity) { return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); } private Object[] grow() { return grow(size + 1); } /** * Returns a capacity at least as large as the given minimum capacity. * Returns the current capacity increased by 50% if that suffices. * Will not return a capacity greater than MAX_ARRAY_SIZE unless * the given minimum capacity is greater than MAX_ARRAY_SIZE. * * @param minCapacity the desired minimum capacity * @throws OutOfMemoryError if minCapacity is less than zero */ private int newCapacity(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity <= 0) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) return Math.max(DEFAULT_CAPACITY, minCapacity); if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return minCapacity; } return (newCapacity - MAX_ARRAY_SIZE <= 0) ? newCapacity : hugeCapacity(minCapacity); }
擴充數組容量時,會成倍增長,如果數據量較大又未開辟足夠的空間,且不斷寫入新數據的時候,ArrayList需要不斷開辟新空間又要復制數據,性能較差且可能浪費內存空間;
在add(),set(),remove()等寫方法中,會看到modCount得增加,這個字段有什麽作用呢?在AbstractList中能看到modCount的定義
/** * The number of times this list has been <i>structurally modified</i>. * Structural modifications are those that change the size of the * list, or otherwise perturb it in such a fashion that iterations in * progress may yield incorrect results. * * <p>This field is used by the iterator and list iterator implementation * returned by the {@code iterator} and {@code listIterator} methods. * If the value of this field changes unexpectedly, the iterator (or list * iterator) will throw a {@code ConcurrentModificationException} in * response to the {@code next}, {@code remove}, {@code previous}, * {@code set} or {@code add} operations. This provides * <i>fail-fast</i> behavior, rather than non-deterministic behavior in * the face of concurrent modification during iteration. * * <p><b>Use of this field by subclasses is optional.</b> If a subclass * wishes to provide fail-fast iterators (and list iterators), then it * merely has to increment this field in its {@code add(int, E)} and * {@code remove(int)} methods (and any other methods that it overrides * that result in structural modifications to the list). A single call to * {@code add(int, E)} or {@code remove(int)} must add no more than * one to this field, or the iterators (and list iterators) will throw * bogus {@code ConcurrentModificationExceptions}. If an implementation * does not wish to provide fail-fast iterators, this field may be * ignored. */ protected transient int modCount = 0;
modCount用來記錄鏈表被修改的次數;典型的場景是:當叠代器對鏈表操作時,其他方法修改了該鏈表,那麽通過對比modCount的值就可以判斷鏈表在使用的時候是否被修改過,如果被修改過,立即拋出ConcurrentModificationException異常。這就是AbstractList的fail-fast機制,而不是出現不確定的行為。
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
checkForComodification()方法定義在ArrayList的私有內部類中,叠代器常用的next方法和remove方法都會調用該方法,用於快速檢測鏈表是否被其他行為修改
Java集合--ArrayList源碼