自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

可惡!簡單的刪除集合中的元素竟然報錯

開發(fā) 后端
什么是快速失?。篺ail-fast 機制是java集合(Collection)中的一種錯誤機制。它只能被用來檢測錯誤,因為JDK并不保證fail-fast機制一定會發(fā)生。當多個線程對同一個集合的內(nèi)容進行操作時,就可能會產(chǎn)生fail-fast事件。

前言

什么是快速失?。篺ail-fast 機制是java集合(Collection)中的一種錯誤機制。它只能被用來檢測錯誤,因為JDK并不保證fail-fast機制一定會發(fā)生。當多個線程對同一個集合的內(nèi)容進行操作時,就可能會產(chǎn)生fail-fast事件。

運行如下代碼,即可出現(xiàn)異常:

  1. // 關(guān)于fail-fast的一些思考 
  2. public class FailFastTest { 
  3.     public static void main(String[] args) { 
  4.         // 構(gòu)建ArrayList 
  5.         List<Integer> list = new ArrayList<>(); 
  6.         list.add(1); 
  7.         list.add(2); 
  8.         list.add(3); 
  9.         list.add(4); 
  10.         for (int i : list) { 
  11.             list.remove(1); 
  12.         } 
  13.     } 

控制臺會輸出如下異常:

為什么要報這個錯?途中出錯的地方是ArrayList中的代碼,定位到該處代碼:

  1. final void checkForComodification() { 
  2.     if (modCount != expectedModCount) 
  3.         throw new ConcurrentModificationException(); 

modCount是這個集合修改的次數(shù),這個屬性來自AbstractList,而我們的ArrayList是繼承了該抽象類的。

  1. protected transient int modCount = 0; 

expectedModCount又是啥呢?當我們進行遍歷時候debug一下發(fā)現(xiàn)進行forEach循環(huán)的時候其實走了下面這個方法iterator,而且遍歷這個底層還是走的hasNext方法

  1. public Iterator<E> iterator() { 
  2.     return new Itr(); 

判斷是否有下一個元素

  1. public boolean hasNext() { 
  2.             return cursor != size
  3.         } 

next()方法用于獲取元素

  1. public E next() { 
  2.          checkForComodification(); // 留意這個方法 
  3.          int i = cursor
  4.          if (i >= size
  5.              throw new NoSuchElementException(); 
  6.          Object[] elementData = ArrayList.this.elementData; 
  7.          if (i >= elementData.length) 
  8.              throw new ConcurrentModificationException(); 
  9.          cursor = i + 1; 
  10.          return (E) elementData[lastRet = i]; 
  11.      } 

點進這個new Itr(),驚喜的發(fā)現(xiàn)原來這個expectedModCount是在這里被賦值的而且和modCount一樣

  1. private class Itr implements Iterator<E> { 
  2.         int cursor;       // index of next element to return 
  3.         int lastRet = -1; // index of last element returned; -1 if no such 
  4.         int expectedModCount = modCount; // 注意:此處進行賦值 
  5.         ...... 
  6.         ...... 

接下來看下ArrayList的remove()方法,其對modCount進行了增加,這是導致報錯的原因

  1. public E remove(int index) { 
  2.     rangeCheck(index); 
  3.  
  4.     modCount++; // 對modCount進行了++的操作 
  5.     E oldValue = elementData(index); 
  6.  
  7.     int numMoved = size - index - 1; 
  8.     if (numMoved > 0) 
  9.         System.arraycopy(elementData, index+1, elementData, index
  10.                          numMoved); 
  11.     elementData[--size] = null; // clear to let GC do its work 
  12.  
  13.     return oldValue; 

上面的next()方法這有調(diào)用一個checkForComodification()方法,下面貼一下這方法的代碼

  1. final void checkForComodification() { 
  2.     if (modCount != expectedModCount) 
  3.         throw new ConcurrentModificationException(); 

ArrayList里面remove()方法進行了modCount++操作,原來是我們對集合進行操作后改變了modCount導致上面代碼成立,從而拋出異常

但是當我們使用Itr類的remove,也就是如下代碼進行對元素改動時,不會拋出ConcurrentModificationException異常

  1. public void remove() { 
  2.        if (lastRet < 0) 
  3.            throw new IllegalStateException(); 
  4.        checkForComodification(); 
  5.  
  6.        try { 
  7.            ArrayList.this.remove(lastRet); 
  8.            cursor = lastRet; 
  9.            lastRet = -1; 
  10.            // 將ArrayList的modCount賦值給Itr類的expectedModCount  
  11.            //這樣再次調(diào)用next方法時就不會出現(xiàn)這倆個值不一致 從而避免報錯 
  12.            expectedModCount = modCount;  
  13.        } catch (IndexOutOfBoundsException ex) { 
  14.            throw new ConcurrentModificationException(); 
  15.        } 
  16.    } 

與ArrayList的remove()方法不同的是,該remove()方法調(diào)用ArrayList.this.remove(lastRet);后顯然modCount++了,但是馬上又讓expectedModCount = modCount就是這樣才不會拋出異常。

梳理整個流程:

1、for循環(huán)遍歷實質(zhì)上調(diào)用的是Itr類的方法進行遍歷(Itr類實現(xiàn)了Iterator)

2、Itr類在構(gòu)造的時候會將ArrayList的modCount(實際上modCount是AbstractList的屬性,但是ArrayList繼承了AbstractList)賦值給Itr類的expectedModCount

3、for循環(huán)中調(diào)用的remove()方法時ArrayList的,這個方法會對modCount進行++操作

4、remove方法調(diào)用后,繼續(xù)遍歷會調(diào)用Itr的next()方法,而這個next()方法中的checkForComodification()方法會對modCount和expectedModCount進行對比,由于remove方法已經(jīng)操作過modCount因此這倆個值不會相等,故報錯。

如何改進?

1、可以使用Itr中的remove方法進行改進,改進代碼如下

  1. public static void main(String[] args) { 
  2.     // 構(gòu)建ArrayList 
  3.     List<Integer> list = new ArrayList<>(); 
  4.     list.add(1); 
  5.     list.add(2); 
  6.     list.add(3); 
  7.     list.add(4); 
  8.     Iterator<Integer> iterator = list.iterator(); 
  9.     while(iterator.hasNext()) { 
  10.         iterator.next(); 
  11.         iterator.remove(); 
  12.     } 
  13.     System.out.println(list.size()); // 0 

2、使用CopyOnWriterArrayList來代替Arraylist,它對ArrayList的操作時會先復(fù)制一份數(shù)據(jù)出來操作完了再將其更新回去替換掉舊的,所以CopyOnWrite容器只能保證數(shù)據(jù)的最終一致性,不能保證數(shù)據(jù)的實時一致性。這是采用了CopyOnWriterArrayList的fail-safe機制,當集合的結(jié)構(gòu)被改變的時候,fail-safe機制會在復(fù)制原集合的一份數(shù)據(jù)出來,然后在復(fù)制的那份數(shù)據(jù)遍歷,fail-safe機制,在JUC包的集合都是有這種機制實現(xiàn)的。

雖然fail-safe不會拋出異常,但存在以下缺點

1、復(fù)制時需要額外的空間和時間上的開銷。

2、不能保證遍歷的是最新內(nèi)容。

總結(jié)

 

對于fail-fast機制,我們要操作List集合時可以使用Iterator的remove()方法在遍歷過程中刪除元素,或者使用fail-safe機制的CopyOnWriterArrayList,當然使用的時候需要權(quán)衡下利弊,結(jié)合相關(guān)業(yè)務(wù)場景。

 

責任編輯:武曉燕 來源: 程序員巴士
相關(guān)推薦

2012-05-29 15:29:14

JavaArrayList

2012-03-19 09:57:09

JavaArrayList

2020-12-28 07:47:35

動態(tài)代理AOP

2010-07-27 15:14:08

刪除telnet

2020-08-06 07:49:57

List元素集合

2024-12-03 08:43:49

2015-08-04 09:18:26

JavaArrayList元素

2020-09-30 14:24:58

PythonSet對象

2015-03-25 11:42:52

Java刪除特定元素

2021-12-08 10:36:46

JavaPDF文件

2022-04-27 09:40:25

抓包圖四次揮手TCP

2023-03-27 08:34:00

配置容器Spring

2021-12-09 09:02:53

JavaPDF文件iText

2010-05-24 15:07:52

Swap space

2010-11-22 12:14:55

MySQL字段

2012-01-12 13:24:55

Java

2022-01-20 09:58:44

Python元素列表

2021-07-22 09:53:34

Vector類Java添加元素

2010-11-10 11:51:04

SQL SERVER級

2019-02-13 14:55:22

Windows 10視頻刪除聲音
點贊
收藏

51CTO技術(shù)棧公眾號