ArrayList的擴容規則是變成原來最大容量的1.5倍+1
具體爲什麼,現在看一下源碼:
ensureCapacityInternal是判斷是否要擴容的方法,下面是源碼:
elementData是存放元素的對象數組
EMPTY_ELEMENTDATA是空數組,表示現在ArrayList是空的
ensureCapacityInternal中首先是判斷現在的ArrayList是不是空的,如果是空的,minCapacity就取默認的容量和傳入的參數minCapacity中的大值
然後調用ensureExplicitCapacity方法,
modCount是fail fast機制,在jdk1.6之前都是有volatile來修飾的,儘可能的讓併發訪問非安全的集合對象時儘快的失敗拋出異常,讓程序員修改代碼。
在jdk1.7中去掉了volatile修飾,因爲感覺沒有必要爲非線程安全集合浪費效率,在jdk1.5開始就提供了線程安全的集合類,在多線程環境下就應該使用線程安全的集合。
接着看,如果minCapacity的值大於add數據之前的大小,就調用grow方法,進行擴容,否則什麼也不做。
下面是具體看出來擴容機制的大小增長規則了:
注意:這裏傳過來的minCapcatiy的值是size+1,能夠實現grow方法調用就肯定是(size+1)>elementData.length的情況,所以size就是初始最大容量或上一次擴容後達到的最大容量,所以纔會進行擴容。
newCapacity=oldCapacity+(oldCapacity>>1),這裏就是擴容大小確定的地方,相當於新的最大容量是 size+1+size/2 相當於原來的1.5倍然後加1
==============================================================================================================================
這樣每次size達到容量後,再插入數據就會造成擴容,默認的容量大小是10,如果我們現在連續插入17的對象,就會擴容兩次,第一次是在插入第11個對象時,第二次是在插入第17個對象時擴容,這樣會頻繁進行數組的拷貝,效率影響很大。那麼我們在插入數據數量已知的情況,可以調用ensureCapacity方法。
下面是方法源碼:
爲什麼要先判斷先判斷elementData!=EMPTY_ELEMENTDATA呢?現在看一下ArrayList的無參構造函數
這裏看出來ArrayList在沒有指定初始化容量的時候elementData就是一個空對象數組,沒有任何對象元素,也就是容量爲0,沒有分配空間。
可以再看一下add方法中,調用的ensureCapacityInternal方法中判斷elementData == EMPTY_ELEMENTDATA 是否爲空,如果爲空,minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);,minCapacity設置爲其中的大值,在默認是空的時候,初次調用add方法傳入的參數是1,也就是這裏的minCapacity就是1,現在minCapcity變成了DEFAULT_CAPACITY 也就是10,下面會調用grow方法,然後執行 elementData = Arrays.copyOf(elementData, 10);下面注意拷貝方法:
執行了第一條語句後,copy=new Objecet[10]了,返回的結果數組大小就是10了,ArrayList相當於在沒指定initialCapacity時就是會使用延遲分配對象數組空間,當第一次插入元素時才分配10個對象空間。
上面分析過程也包含了擴容的步驟了。
當我們已經確定了要插入的對象的數目(並不是在創建ArrayList之前就知道有多少對象要插入的情況),就應該首先調用ensureCapacity來一次性擴容到可以容得下要插入的對象個數,這樣就避免的多次進行數組拷貝的情況,提高了效率,算是優化吧
當然,我們在創建ArrayList之前就知道有多少對象要插入就使用有參構造。