深入了解ArrayList集合的初始容量和初始容量為0的兩種擴容機制(1.8版本)
網絡上對于ArrayList集合的空參構造是否為0,存在不同的的看法。對此,分析了源碼,有以下見解:
1.空參構造,集合初始容量必定為0,添加一個元素,擴容為10。
2.有參構造,參數為0和集合長度為0時,初始容量為0,添加一個元素擴容為1,再添加一個元素擴容為2,再添加一個元素擴容
為3,再添加一個元素擴容為4,再添加一個元素擴容為6...
- 版本不同
- ArrayList的三種構造方法
- ArrayList()的初始容量和擴容
- ArrayList(int initialCapacity)的初始容量擴容
- ArrayList(Collection( extends E) c)(尖括号顯示不出來,用()代替)
- ArrayList集合的擴容公式
版本不同
JAVA對于ArrayList集合進行了優化,目前所知,優化了空參構造的初始容量和擴容公式,至于是1.7還是1.8版本開始,
就不清楚了。在1.6版本之時,該集合的空參構造的初始容量,确實是10,源碼自己找。
該文的源碼以JDK1.8為主。
ArrayList的三種構造方法
public ArrayList(int initialCapacity)
public ArrayList()
public ArrayList(Collection<? extends E> c)
public ArrayList()的初始容量和擴容
首先,分析空參構造的初始容量。
在1.8裡面定義了三個Object[]:
1.用于空執行個體的共享空數組執行個體。
private static final Object[] EMPTY_ELEMENTDATA = {}
2.用于預設大小的空執行個體的共享空數組執行個體。
該數組與EMPTY_ELEMENTDATA差別,但都為空,以便知道當添加第一個元素時,該數組應該于何時擴容多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}
而這兩個數組,有什麼關系呢?
第一個數組運用于有參構造方法的參數為0或者集合長度為0的時候,而第二個數組運用于空參構造的時候。
這兩個數組一定要差別,對于以後的擴容造成影響。
ArrayList的底層是由(transient Object[] elementData)實作的; 該數組是存儲ArrayList元素的數組緩沖區。ArrayList的容量是這個數組緩沖區的長度。
任何的elementData=DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空集合,當添加第一個元素時,将被擴充到DEFAULT_CAPACITY。
- 初始容量
要知道空參構造的初始容量,要對空參構造的源碼進行分析:
public ArrayList() {
this.elementData =
DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
這樣看空參構造,初始容量就是0。
- 擴容機制
那麼當添加一個元素的時候,集合的容量會擴容到預設容量為10,也可能為大于10的值,視情況而定。一般預設一次隻添加一個元素。
private int newCapacity(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
//這裡就定義了當
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA數組添加元素的情況,若是第一次添加:1.隻添加一個元素,
//那麼容量就擴容為DEFAULT_CAPACITY=10;2.要是添加的元素大于該預設容量,則以該元素個數為
//擴容後的容量。
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
public ArrayList(int initialCapacity)的初始容量和擴容
- 初始容量
這裡就讨論當參數為0的時候,該集合的初始容量也為0。
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
//當該參數為0,底層數組為EMPTY_ELEMENTDATA,那麼該數組的長度為0,集合初始容量則為0。
} else {
throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
}
}
-
擴容機制
加進一個元素,擴容為1。
源碼分析:
//add源碼
public boolean add(E e) {
modCount++;
add(e, elementData, size);//調用add(E e, Object[] elementData, int s)方法
return true;
}
//添加一個元素,調用此方法
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();// 數組長度為0,繼續調用grow()方法
elementData[s] = e;
size = s + 1;
}
private Object[] grow() {
return grow(size + 1);//grow()調用grow(1)
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
//擴容方法newCapacity(minCapacity)
}
//擴容
private int newCapacity(int minCapacity) {
int oldCapacity = elementData.length;//oldCapacity=0
int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity=0
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
//DEFAULTCAPACITY_EMPTY_ELEMENTDATA空參構造的數組;
//抱歉這是為了空參構造設計的,參數為0和集合長度為0對應的數組是EMPTY_ELEMENTDATA。
if (minCapacity < 0)
throw new OutOfMemoryError();
return minCapacity;//上述條件都不符合,那麼就傳回1.
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity//當newCapacity - minCapacity > 0,且小于最大的數組長度,就傳回原數組的1.5倍
: hugeCapacity(minCapacity);
}
是以當添加一個元素進去時,該集合擴容為1。
public ArrayList(Collection(? extends E) c)
該構造方法與 ArrayList(int initialCapacity)方法有一樣的地方:當參數集合長度為0時,該集合的初始容量也為0。
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
//當該集合為空時,同 ArrayList(int initialCapacity)方法的參數為0情況,集合的初始容量為0。
擴容情況參照ArrayList(0)的情況,同樣。
ArrayList集合的擴容公式
1.6版本的擴容公式:
數組原長度*3/2+1
.
1.6之後的擴容公式:
oldCapacity + (oldCapacity >> 1)
因為位運算快。
之是以寫此博文,是因為,看見一個拿着1.8版本的源碼,睜眼說瞎話,說空參構造的初始容量為10,這簡直就是害人。不平之下
, 按照自己對于源碼的見解,進行分析,望能幫助到需要之人。
若有不足之處,望批評指正。