成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

前言

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

運行如下代碼,即可出現異常:

  1. // 關于fail-fast的一些思考 
  2. public class FailFastTest { 
  3.     public static void main(String[] args) { 
  4.         // 構建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是這個集合修改的次數,這個屬性來自AbstractList,而我們的ArrayList是繼承了該抽象類的。

  1. protected transient int modCount = 0; 

expectedModCount又是啥呢?當我們進行遍歷時候debug一下發現進行forEach循環的時候其實走了下面這個方法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(),驚喜的發現原來這個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()方法這有調用一個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.            //這樣再次調用next方法時就不會出現這倆個值不一致 從而避免報錯 
  12.            expectedModCount = modCount;  
  13.        } catch (IndexOutOfBoundsException ex) { 
  14.            throw new ConcurrentModificationException(); 
  15.        } 
  16.    } 

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

梳理整個流程:

1、for循環遍歷實質上調用的是Itr類的方法進行遍歷(Itr類實現了Iterator)

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

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

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

如何改進?

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

  1. public static void main(String[] args) { 
  2.     // 構建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的操作時會先復制一份數據出來操作完了再將其更新回去替換掉舊的,所以CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。這是采用了CopyOnWriterArrayList的fail-safe機制,當集合的結構被改變的時候,fail-safe機制會在復制原集合的一份數據出來,然后在復制的那份數據遍歷,fail-safe機制,在JUC包的集合都是有這種機制實現的。

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

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

2、不能保證遍歷的是最新內容。

總結

 

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

 

責任編輯:武曉燕 來源: 程序員巴士
相關推薦

2012-05-29 15:29:14

JavaArrayList

2020-12-28 07:47:35

動態代理AOP

2012-03-19 09:57:09

JavaArrayList

2010-07-27 15:14:08

刪除telnet

2024-12-03 08:43:49

2020-08-06 07:49:57

List元素集合

2015-08-04 09:18:26

JavaArrayList元素

2020-09-30 14:24:58

PythonSet對象

2015-03-25 11:42:52

Java刪除特定元素

2021-12-09 09:02:53

JavaPDF文件iText

2021-12-08 10:36:46

JavaPDF文件

2022-04-27 09:40:25

抓包圖四次揮手TCP

2010-05-24 15:07:52

Swap space

2010-11-22 12:14:55

MySQL字段

2023-03-27 08:34:00

配置容器Spring

2012-01-12 13:24:55

Java

2022-01-20 09:58:44

Python元素列表

2019-02-13 14:55:22

Windows 10視頻刪除聲音

2010-11-10 11:51:04

SQL SERVER級

2021-07-22 09:53:34

Vector類Java添加元素
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产不卡在线播放 | 91精品国产综合久久久久久丝袜 | 欧美99| 国产精品日韩欧美一区二区 | 欧美区日韩区 | 日日干日日操 | 日韩在线播放视频 | 国产露脸国语对白在线 | 国产成人亚洲精品 | 精品国产免费一区二区三区演员表 | 久久久免费少妇高潮毛片 | 鸳鸯谱在线观看高清 | 亚洲国产高清高潮精品美女 | 91激情电影| 亚洲巨乳自拍在线视频 | 最新高清无码专区 | 国产探花在线观看视频 | 午夜成人在线视频 | 国产精品一区在线观看 | 男女爱爱网站 | 精品视频一区二区 | 久久精品视频一区二区三区 | 亚洲综合一区二区三区 | 另类专区亚洲 | 日日躁狠狠躁aaaaxxxx | 成人不卡在线 | 久久天堂网 | 一级毛片视频在线观看 | 久久久久久久电影 | a国产一区二区免费入口 | 国产精彩视频 | 亚洲综合大片69999 | 伊人春色在线 | 中文字幕免费观看 | 国产一区二区三区视频 | 精品国产欧美一区二区 | 日韩欧美在线不卡 | 观看av | 久久久久久亚洲精品 | 国产精品一区二区在线 | 亚洲免费视频网址 |