Java動(dòng)態(tài)數(shù)組ArrayList實(shí)現(xiàn)動(dòng)態(tài)原理
簡(jiǎn)介
ArrayList是一個(gè)動(dòng)態(tài)數(shù)組,可以通過(guò)泛型來(lái)決定數(shù)據(jù)中存放的元素類型
怎么實(shí)現(xiàn)的動(dòng)態(tài)
之所以是動(dòng)態(tài)數(shù)組,是因?yàn)樵谑褂脽o(wú)參構(gòu)造函數(shù)創(chuàng)建ArrayList之后,默認(rèn)為空數(shù)組
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
在你每次去add一個(gè)元素的時(shí)候,通過(guò)ensureCapacityInternal方法去檢查添加后元素的個(gè)數(shù)是否會(huì)超出當(dāng)前數(shù)組的長(zhǎng)度,如果超出,數(shù)組將會(huì)進(jìn)行擴(kuò)容,以滿足添加數(shù)據(jù)的需求
public boolean add(E e) { // size為當(dāng)前數(shù)據(jù)存放元素的數(shù)量 ensureCapacityInternal(size + 1); // elementData為ArrayList內(nèi)部存放數(shù)據(jù)的數(shù)組 elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { // 判斷數(shù)組是否為空,為空則將最小容量設(shè)置為默認(rèn)值10 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } // 判斷當(dāng)前容量是否滿足最小容量 ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { // modCount用于記錄修改次數(shù),作用于fast-fail機(jī)制 // 如果在迭代的時(shí)候發(fā)現(xiàn)modCount被修改了,則會(huì)拋出異常來(lái)避免可能會(huì)出現(xiàn)的不確定行為 modCount++; // 如果當(dāng)前容量小于最小容量,則進(jìn)行擴(kuò)容 if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { int oldCapacity = elementData.length; // 計(jì)算新的容量,擴(kuò)大為原容量的1.5倍, oldCapacity >> 1 運(yùn)算表示右移一位,也就是相當(dāng)于除以二并取整 int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果新容量超過(guò)了數(shù)組的最大容量,調(diào)用hugeCapacity方法來(lái)獲取一個(gè)更大的容量 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 拷貝原數(shù)組,將原數(shù)組的元素復(fù)制到新數(shù)組中 elementData = Arrays.copyOf(elementData, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) throw new OutOfMemoryError(); // 如果最小容量大于最大容量,則將容量設(shè)置為Integer.MAX_VALUE return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
從上面的源碼解釋可以看到,如果新建數(shù)據(jù)沒(méi)有給定初始值,ArrayList是每次在進(jìn)行add操作的時(shí)候去進(jìn)行擴(kuò)容的,擴(kuò)容則會(huì)涉及到Arrays.copyOf深拷貝,會(huì)造成內(nèi)存和性能的消耗,所以在生產(chǎn)項(xiàng)目中,推薦大家盡量在創(chuàng)建ArrayList對(duì)象的時(shí)候就指定其容量。
Fast-Fail機(jī)制
大家肯定注意到了上面提到的通過(guò)modCount實(shí)現(xiàn)的Fast-Fail機(jī)制
Fast-Fail是一種機(jī)制,用于檢測(cè)在迭代過(guò)程中是否有其他線程對(duì)集合進(jìn)行了修改。當(dāng)一個(gè)線程在迭代 ArrayList 時(shí),如果其他線程對(duì) ArrayList 進(jìn)行了結(jié)構(gòu)性修改(如添加、刪除元素),那么迭代器會(huì)立即拋出 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)
造成快速失敗的常見(jiàn)操作包括:
- 在迭代過(guò)程中,使用 ArrayList 的 add、remove、clear 等方法修改集合的結(jié)構(gòu)。
- 多個(gè)線程同時(shí)對(duì) ArrayList 進(jìn)行修改操作。
為了避免快速失敗,可以采取以下措施:
- 在迭代過(guò)程中,不要修改集合的結(jié)構(gòu)。如果需要修改,可以使用迭代器的 remove 方法,對(duì)應(yīng)上面的例子也就是iterator.remove()。
- 在多線程環(huán)境下,對(duì)于需要并發(fā)修改的情況,可以使用線程安全的集合類,如 CopyOnWriteArrayList。
如果使用語(yǔ)法糖 for (String next : list) {
System.out.println(next);
list.remove(next);
} 這樣的遍歷方式,其實(shí)內(nèi)部也是使用的迭代器,所以會(huì)存在同樣的問(wèn)題
到此這篇關(guān)于Java動(dòng)態(tài)數(shù)組ArrayList實(shí)現(xiàn)動(dòng)態(tài)原理的文章就介紹到這了,更多相關(guān)Java ArrayList實(shí)現(xiàn)動(dòng)態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot關(guān)閉druid監(jiān)控 druid2改配置文件無(wú)效的解決
這篇文章主要介紹了springboot關(guān)閉druid監(jiān)控 druid2改配置文件無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Spring Boot攔截器實(shí)現(xiàn)步驟及測(cè)試實(shí)例
這篇文章主要介紹了Spring Boot攔截器實(shí)現(xiàn)步驟及測(cè)試實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04Struts2 Result 返回JSON對(duì)象詳解
這篇文章主要講解Struts2返回JSON對(duì)象的兩種方式,講的比較詳細(xì),希望能給大家做一個(gè)參考。2016-06-06java分頁(yè)攔截類實(shí)現(xiàn)sql自動(dòng)分頁(yè)
這篇文章主要為大家詳細(xì)介紹了java分頁(yè)攔截類可以實(shí)現(xiàn)sql自動(dòng)分頁(yè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Java多線程編程實(shí)戰(zhàn)之模擬大量數(shù)據(jù)同步
這篇文章主要介紹了Java多線程編程實(shí)戰(zhàn)之模擬大量數(shù)據(jù)同步,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02Java中實(shí)現(xiàn)定時(shí)任務(wù)的兩種方法舉例詳解
這篇文章主要給大家介紹了關(guān)于Java中實(shí)現(xiàn)定時(shí)任務(wù)的兩種方法,文中總結(jié)了各種實(shí)現(xiàn)方式的優(yōu)缺點(diǎn),并給出了推薦的使用場(chǎng)景,通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-12-12