Java動態(tài)數(shù)組ArrayList實現(xiàn)動態(tài)原理
簡介
ArrayList是一個動態(tài)數(shù)組,可以通過泛型來決定數(shù)據(jù)中存放的元素類型
怎么實現(xiàn)的動態(tài)
之所以是動態(tài)數(shù)組,是因為在使用無參構造函數(shù)創(chuàng)建ArrayList之后,默認為空數(shù)組
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
在你每次去add一個元素的時候,通過ensureCapacityInternal方法去檢查添加后元素的個數(shù)是否會超出當前數(shù)組的長度,如果超出,數(shù)組將會進行擴容,以滿足添加數(shù)據(jù)的需求
public boolean add(E e) { // size為當前數(shù)據(jù)存放元素的數(shù)量 ensureCapacityInternal(size + 1); // elementData為ArrayList內(nèi)部存放數(shù)據(jù)的數(shù)組 elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { // 判斷數(shù)組是否為空,為空則將最小容量設置為默認值10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } // 判斷當前容量是否滿足最小容量 ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { // modCount用于記錄修改次數(shù),作用于fast-fail機制 // 如果在迭代的時候發(fā)現(xiàn)modCount被修改了,則會拋出異常來避免可能會出現(xiàn)的不確定行為 modCount++; // 如果當前容量小于最小容量,則進行擴容 if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { int oldCapacity = elementData.length; // 計算新的容量,擴大為原容量的1.5倍, oldCapacity >> 1 運算表示右移一位,也就是相當于除以二并取整 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果新容量超過了數(shù)組的最大容量,調(diào)用hugeCapacity方法來獲取一個更大的容量 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 拷貝原數(shù)組,將原數(shù)組的元素復制到新數(shù)組中 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); // 如果最小容量大于最大容量,則將容量設置為Integer.MAX_VALUE return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
從上面的源碼解釋可以看到,如果新建數(shù)據(jù)沒有給定初始值,ArrayList是每次在進行add操作的時候去進行擴容的,擴容則會涉及到Arrays.copyOf深拷貝,會造成內(nèi)存和性能的消耗,所以在生產(chǎn)項目中,推薦大家盡量在創(chuàng)建ArrayList對象的時候就指定其容量。
Fast-Fail機制
大家肯定注意到了上面提到的通過modCount實現(xiàn)的Fast-Fail機制
Fast-Fail是一種機制,用于檢測在迭代過程中是否有其他線程對集合進行了修改。當一個線程在迭代 ArrayList 時,如果其他線程對 ArrayList 進行了結構性修改(如添加、刪除元素),那么迭代器會立即拋出 ConcurrentModificationException 異常,以避免出現(xiàn)不確定的行為。 例子:
public class Example1 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(3); list.add("qwe"); list.add("asd"); list.add("zxc"); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ String next = iterator.next(); System.out.println(next); list.remove(next); } } } 輸出: qwe Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at com.example.lepractice.collection.Example1.main(Example1.java:21)
造成快速失敗的常見操作包括:
- 在迭代過程中,使用 ArrayList 的 add、remove、clear 等方法修改集合的結構。
- 多個線程同時對 ArrayList 進行修改操作。
為了避免快速失敗,可以采取以下措施:
- 在迭代過程中,不要修改集合的結構。如果需要修改,可以使用迭代器的 remove 方法,對應上面的例子也就是iterator.remove()。
- 在多線程環(huán)境下,對于需要并發(fā)修改的情況,可以使用線程安全的集合類,如 CopyOnWriteArrayList。
如果使用語法糖 for (String next : list) {
System.out.println(next);
list.remove(next);
} 這樣的遍歷方式,其實內(nèi)部也是使用的迭代器,所以會存在同樣的問題
到此這篇關于Java動態(tài)數(shù)組ArrayList實現(xiàn)動態(tài)原理的文章就介紹到這了,更多相關Java ArrayList實現(xiàn)動態(tài)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
springboot關閉druid監(jiān)控 druid2改配置文件無效的解決
這篇文章主要介紹了springboot關閉druid監(jiān)控 druid2改配置文件無效的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05Java多線程編程實戰(zhàn)之模擬大量數(shù)據(jù)同步
這篇文章主要介紹了Java多線程編程實戰(zhàn)之模擬大量數(shù)據(jù)同步,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-02-02