日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Java 如何繞過迭代器遍歷時的數據修改異常

瀏覽:200日期:2022-08-16 16:05:39
前言

既然是繞過迭代器遍歷時的數據修改異常,那么有必要先看一下是什么樣的異常。如果在集合的迭代器遍歷時嘗試更新集合中的數據,比如像下面這樣,我想輸出 Hello,World,Java,迭代時卻發現多了一個 C++ 元素,如果直接刪除掉的話。

List<String> list = new ArrayList<>();Collections.addAll(list, 'Hello', 'World', 'C++', 'Java');// 我想輸出 Hello,World,Java,迭代時發現多一個 C++,所以直接刪除掉。Iterator iterator = list.iterator();System.out.println(iterator.next());System.out.println(iterator.next());list.remove('C++');System.out.println(iterator.next());

那么我想你一定會遇到一個異常 ConcurrentModificationExceptio 。

HelloWorldjava.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:907)at java.util.ArrayList$Itr.next(ArrayList.java:857)at com.wdbyte.lab.jdk.ModCountDemo.updateCollections(ModCountDemo.java:26)

這個異常在剛開始學習 Java 或者使用其他的非線程安全的集合過程中可能都有遇到過。導致這個報錯出現的原因就和我們操作的一樣,對于某些集合,不建議在遍歷時進行數據修改,因為這樣會數據出現不確定性。

那么如何繞過這個錯誤呢?這篇文章中腦洞大開的三種方式一定不會讓你失望。

異常原因

這不是一篇源碼分析的文章,但是為了介紹繞過這個異常出現的原因,還是要提一下的,已經知道的同學可以直接跳過。

根據上面的報錯,可以追蹤到報錯位置 ArrayList.java 的 857 行和 907 行,追蹤源碼可以發現在迭代器的 next 方法的第一行,調用了 checkForComodification() 方法。

Java 如何繞過迭代器遍歷時的數據修改異常

而這個方法直接進行了一個把變量 modCount 和 expectedModCount 進行了對比,如果不一致就會拋出來 ConcurrentModificationException 異常。

final void checkForComodification() { if (modCount != expectedModCount)throw new ConcurrentModificationException();}

那么 modCount 這個變量存儲的是什么信息呢?

/** * The number of times this list has been <i>structurally modified</i>. * Structural modifications are those that change the size of the * list, or otherwise perturb it in such a fashion that iterations in * progress may yield incorrect results. * * <p>This field is used by the iterator and list iterator implementation * returned by the {@code iterator} and {@code listIterator} methods. * If the value of this field changes unexpectedly, the iterator (or list * iterator) will throw a {@code ConcurrentModificationException} in * response to the {@code next}, {@code remove}, {@code previous}, * {@code set} or {@code add} operations. This provides * <i>fail-fast</i> behavior, rather than non-deterministic behavior in * the face of concurrent modification during iteration. * * <p><b>Use of this field by subclasses is optional.</b> If a subclass * wishes to provide fail-fast iterators (and list iterators), then it * merely has to increment this field in its {@code add(int, E)} and * {@code remove(int)} methods (and any other methods that it overrides * that result in structural modifications to the list). A single call to * {@code add(int, E)} or {@code remove(int)} must add no more than * one to this field, or the iterators (and list iterators) will throw * bogus {@code ConcurrentModificationExceptions}. If an implementation * does not wish to provide fail-fast iterators, this field may be * ignored. */protected transient int modCount = 0;

直接看源碼注釋吧,直接翻譯一下意思就是說 modCount 數值記錄的是列表的結構被修改的次數,結構修改是指那些改變列表大小的修改,或者以某種方式擾亂列表,從而使得正在進行的迭代可能產生不正確的結果。同時也指出了這個字段通常會在迭代器 iterator 和 listIterator 返回的結果中使用,如果 modCount 和預期的值不一樣,會拋出 ConcurrentModificationException 異常。

而上面與 modCount 進行對比的字段 expectedModCount 的值,其實是在創建迭代器時,從 modCount 獲取的值。如果列表結構沒有被修改過,那么兩者的值應該是一致的。

繞過方式一:40 多億次循環繞過

上面分析了異常產生的位置和原因,是因為 modCount 的當前值和創建迭代器時的值有所變化。所以第一種思路很簡單,我們只要能讓兩者的值一致就可以了。在源碼 int modCount = 0; 中可以看到 modCount 的數據類型是 INT ,既然是 INT ,就是有數據范圍,每次更新列表結構 modCount 都會增1,那么是不是可以增加到 INT 數據類型的值的最大值溢出到負數,再繼續增加直到變回原來的值呢?如果可以這樣,首先要有一種操作可以在更新列表結構的同時不修改數據。為此翻閱了源碼尋找這樣的方法。還真的存在這樣的方法。

public void trimToSize() { modCount++; if (size < elementData.length) {elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); }}

上來就遞增了 modCount,同時沒有修改任何數據,只是把數據的存儲進行了壓縮。

List<String> list = new ArrayList<>();Collections.addAll(list, 'Hello', 'World', 'C++', 'Java');list.listIterator();Iterator iterator = list.iterator();System.out.println(iterator.next());System.out.println(iterator.next());list.remove('C++');// 40 多億次遍歷,溢出到負數,繼續溢出到原值for (int n = Integer.MIN_VALUE; n < Integer.MAX_VALUE; n++) ((ArrayList) list).trimToSize();System.out.println(iterator.next());

正確輸出了想要的 Hello,World,Java 。

繞過方式二:線程加對象鎖繞過

分析一下我們的代碼,每次輸出的都是 System.out.println(iterator.next());。可以看出來是先運行了迭代器 next 方法,然后才運行了System.out 進行輸出。所以第二種思路是先把第三個元素C++ 更新為Java ,然后啟動一個線程,在迭代器再次調用 next 方法后,把第四個元素移除掉。這樣就輸出了我們想要的結果。

List<String> list = new ArrayList<>();Collections.addAll(list, 'Hello', 'World', 'C++', 'Java');list.listIterator();Iterator iterator = list.iterator();System.out.println(iterator.next());System.out.println(iterator.next());// 開始操作list.set(2, 'Java');Phaser phaser = new Phaser(2);Thread main = Thread.currentThread();new Thread(() -> { synchronized (System.out) {phaser.arriveAndDeregister();while (main.getState() != State.BLOCKED) { try {Thread.sleep(100); } catch (InterruptedException e) {e.printStackTrace(); }}list.remove(3); }}).start();phaser.arriveAndAwaitAdvance();System.out.println(iterator.next());// 輸出集合System.out.println(list);/** * 得到輸出 * * Hello * World * Java * [Hello, World, Java] */

正確輸出了想要的 Hello,World,Java 。這里簡單說一下代碼中的思路,Phaser 是 JDK 7 的新增類,是一個階段執行處理器。構造時的參數 parties 的值為2,說明需要兩個參與方完成時才會進行到下一個階段。而 arriveAndAwaitAdvance 方法被調用時,可以讓一個參與方到達。

所以線程中對 System.out 進行加鎖,然后執行 arriveAndAwaitAdvance 使一個參與方報告完成,此時會阻塞,等到另一個參與方報告完成后,線程進入到一個主線程不為阻塞狀態時的循環。

這時主線程執行 System.out.println(iterator.next()); 。獲取到迭代器的值進行輸出時,因為線程內的加鎖原因,主線程會被阻塞。知道線程內把集合的最后一個元素移除,線程處理完成才會繼續。

繞過方式三:利用類型擦除放入魔法對象

在創建集合的時候為了減少錯誤概率,我們會使用泛型限制放入的數據類型,其實呢,泛型限制的集合在運行時也是沒有限制的,我們可以放入任何對象。所以我們可以利用這一點做些文章。

List<String> list = new ArrayList<>();Collections.addAll(list, 'Hello', 'World', 'C++', 'Java');list.listIterator();Iterator iterator = list.iterator();System.out.println(iterator.next());System.out.println(iterator.next());// 開始操作((List)list).set(2, new Object() { public String toString() {String s = list.get(3);list.remove(this);return s; }});System.out.println(iterator.next());

代碼里直接把第三個元素放入了一個魔法對象,重寫了 toString() 方法,內容是返回集合的第四個元素,然后刪除第三個元素,這樣就可以得到想要的 Hello,World,Java 輸出。

上面就是繞過迭代器遍歷時的數據修改報錯的三種方法了,不管實用性如何,我覺得每一種都是大開腦洞的操作,這些操作都需要對某個知識點有一定的了解

以上就是Java 如何繞過迭代器遍歷時的數據修改異常的詳細內容,更多關于Java 遍歷時的數據修改異常的資料請關注好吧啦網其它相關文章!

標簽: Java
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产精品嫩草99av在线| 深夜福利一区| 国产日本精品| 国产精品va视频| 精品高清久久| 久久婷婷丁香| 亚洲欧洲一区二区天堂久久| 91国语精品自产拍| 性欧美长视频| 日韩av一区二| 欧美aaaaaa午夜精品| 精品五月天堂| 激情视频一区二区三区| 亚洲欧洲免费| 国产精品尤物| 99久久久国产精品美女| 性欧美长视频| 欧美a一区二区| 成人日韩在线观看| 亚洲字幕久久| 精品香蕉视频| 蜜桃av一区二区三区电影| 国产免费av一区二区三区| 老司机精品视频在线播放| 亚洲手机视频| 中文字幕免费精品| 亚洲一区资源| 视频一区免费在线观看| 国产日韩欧美一区在线| 激情综合网址| 国产精品最新自拍| 国产免费成人| 天堂а√在线最新版中文在线| 99香蕉国产精品偷在线观看 | 中文字幕系列一区| 中文字幕av亚洲精品一部二部| 久久精品午夜| 免播放器亚洲一区| 人人精品亚洲| 国产精品久久久久久久久久妞妞| 国产一区导航| 99精品视频在线| 高清久久精品| 久久91视频| 国产精品综合色区在线观看| 日韩精品一区第一页| av亚洲免费| 亚洲高清av| 久久久久中文| 欧美色图一区| 欧美 日韩 国产一区二区在线视频| 国产欧美自拍一区| 国产探花一区二区| 日本国产亚洲| 天堂精品久久久久| 99久久夜色精品国产亚洲狼| 成人影视亚洲图片在线| 亚洲精品三级| 亚洲国内精品| 午夜天堂精品久久久久| 欧美国产日韩电影| 麻豆9191精品国产| 蜜桃91丨九色丨蝌蚪91桃色| 欧美二区视频| 午夜久久福利| 国产一区二区精品| 视频一区二区欧美| 国产美女精品| 亚洲午夜免费| 日本在线视频一区二区| 久久精品超碰| 久久三级毛片| 在线一区av| 黄色亚洲免费| 激情五月综合| 亚洲人成在线影院| 久久丁香四色| 亚洲欧美视频| 久久精品网址| 亚洲免费专区| 国产亚洲精品v| 欧美午夜精彩| 久久国产高清| 久久99久久人婷婷精品综合| 久久狠狠婷婷| 精品视频国内| 伊人久久一区| 激情欧美丁香| 国产一区二区三区网| 天使萌一区二区三区免费观看| 国产精品色婷婷在线观看| 新版的欧美在线视频| 国产日韩一区二区三区在线播放| 欧美专区在线| 久久久久国产精品一区二区| 国产精品日本| 在线人成日本视频| 欧美日韩va| 日韩有码av| 亚洲精品九九| 69堂精品视频在线播放| 日韩和欧美一区二区| 欧美日韩中文一区二区| 亚洲精品影视| 亚洲黄色网址| 日韩高清一区| 国户精品久久久久久久久久久不卡 | 国产不卡精品在线| 国产免费成人| 国产 日韩 欧美一区| 国产精区一区二区| 亚洲自啪免费| 深夜视频一区二区| 久久精品国产久精国产| 少妇精品久久久一区二区三区| 日韩精品一卡| 三上亚洲一区二区| 日韩国产91| 亚洲人www| 一区二区三区四区日韩| 欧美搞黄网站| 亚洲免费播放| 欧美日韩国产探花| 精品在线91| 亚洲一区二区三区高清不卡| 欧美日韩激情| 国产精品婷婷| 日韩激情精品| 欧美国产不卡| 成人在线黄色| 日韩在线综合| 日韩午夜免费| 喷白浆一区二区| 亚洲欧美日韩一区在线观看| 欧美精品激情| 亚洲精品自拍| 欧美日韩精品一区二区三区视频| 国产日韩精品视频一区二区三区| 久久精品99国产精品| 韩日一区二区| 91tv亚洲精品香蕉国产一区| 9久re热视频在线精品| 蜜臀a∨国产成人精品| 免费欧美在线视频| 国产精品久久777777毛茸茸| 欧美xxxx中国| 97精品国产一区二区三区| 久久九九精品| 亚洲欧美网站| 国产精品视频3p| 日本久久成人网| 亚洲欧洲日韩精品在线| 国产第一亚洲| 国产中文一区| 国产精品夜夜夜| 在线视频观看日韩| 国产精品三级| 国产一区白浆| 久久影院资源站| 综合激情在线| 精品欧美一区二区三区在线观看| 色综合视频一区二区三区日韩 | 人人香蕉久久| 欧美一区影院| 欧美中文一区二区| 国产精品久久久久久模特 | 蜜臀av国产精品久久久久| 狠狠久久伊人中文字幕| 日韩黄色av| 香蕉精品999视频一区二区| 欧美aa在线视频| 蜜桃一区二区三区在线| 日韩国产一区| 麻豆久久久久久| 视频在线在亚洲| 免费污视频在线一区| 精品国产a一区二区三区v免费| 人人精品久久| 亚洲精品系列| 一区免费视频| 久久亚洲在线| 欧美天堂视频| 国产精品s色| 91精品国产经典在线观看| 国产麻豆综合| av不卡在线| 久久午夜精品一区二区| 欧美理论视频| 成人羞羞在线观看网站| 欧美国产美女| 91亚洲国产成人久久精品| 国产精品二区影院| 欧美国产中文高清| 国产欧美日韩免费观看| 日韩精品欧美精品| 国产精品久久久久av蜜臀 | 国产极品嫩模在线观看91精品| 18国产精品| 麻豆国产一区| se01亚洲视频|