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

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

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

瀏覽:204日期: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久久,三上悠亚国产精品一区二区三区
中文字幕亚洲精品乱码| 伊人久久国产| 亚洲欧美久久| 男人天堂欧美日韩| 亚洲综合二区| 亚洲一区二区日韩| 亚洲精品少妇| 欧美三级第一页| 国产一卡不卡| 精品一区二区三区视频在线播放 | 亚洲bt欧美bt精品777| 97久久中文字幕| 国产伊人久久| 黄色日韩精品| 日韩av资源网| 精品一区二区男人吃奶| 日本精品影院| 综合激情一区| 久久亚洲道色| 欧美日韩精品一本二本三本| 日韩在线视频一区二区三区| 国产精品sm| 久久免费高清| 亚洲tv在线| 国产一区二区三区日韩精品| 午夜久久免费观看| 欧美一区激情| 久久天堂av| 日韩一区二区三免费高清在线观看| 国产精品videosex极品| 久久视频国产| 日本电影久久久| 国产精品专区免费| 免费中文字幕日韩欧美| 久久久精品国产**网站| 免费黄色成人| 国产精品三p一区二区| 久久精选视频| 日韩综合一区二区三区| 亚洲深夜视频| 日本视频在线一区| 亚洲电影在线| 国产精品入口久久| 亚洲一区二区毛片| 久久成人高清| 中文久久精品| 成人午夜亚洲| 97精品国产99久久久久久免费| 秋霞影视一区二区三区| 国产欧美综合一区二区三区| 午夜精品婷婷| 国产精品麻豆久久| 日韩精品免费视频人成| 欧美/亚洲一区| 国产成人免费av一区二区午夜| 亚洲制服欧美另类| 亚洲精品va| 91日韩在线| 国产欧美亚洲精品a| 好吊日精品视频| 电影91久久久| 日韩和欧美一区二区三区| 国产精品试看| 久久久亚洲一区| 国产伦久视频在线观看| 国产精品久久久久9999高清| 亚洲午夜国产成人| 午夜久久一区| 国产91精品对白在线播放| 国产一区二区三区四区大秀| 国产乱子精品一区二区在线观看 | 亚洲一级高清| 91一区二区| 精品久久视频| 欧美极品一区二区三区| 欧美一区不卡| 婷婷精品在线观看| 免费观看在线色综合| 久久亚洲国产| 久久精品中文| 免费一二一二在线视频| 高清av一区| 精品视频一区二区三区四区五区 | 88久久精品| 免费高清在线一区| 亚洲精品123区| av一区二区高清| 91精品一区二区三区综合| 欧美国产小视频| 不卡一二三区| av高清一区| 成人羞羞在线观看网站| 福利一区二区免费视频 | 国产精品xxx在线观看| 国产精品一区二区三区四区在线观看| 日韩高清三区| 日韩欧乱色一区二区三区在线| 亚洲精品在线二区| 日韩欧美中文字幕电影| 日本不卡一二三区黄网| 国产日本久久| 麻豆一区二区三| 成人一二三区| 桃色一区二区| 黄页网站一区| 少妇精品久久久一区二区| 亚洲欧美日韩视频二区| 综合激情视频| 国产伦理久久久久久妇女| 精品一区91| 日韩一区电影| 蜜桃成人av| 首页国产欧美久久| 日韩国产欧美一区二区三区| 国产另类在线| 中文字幕成在线观看| 欧美99久久| 日韩一区二区免费看| 蜜桃视频一区二区| 欧美日韩一区二区三区不卡视频 | 九九久久国产| 成人三级高清视频在线看| 久久久精品五月天| 天堂成人国产精品一区| 日韩国产精品久久久久久亚洲| 免费在线观看一区| 欧美gv在线| 亚洲欧洲免费| 精品亚洲自拍| 亚洲少妇诱惑| 欧美日韩亚洲一区三区| 久久婷婷国产| 久久精品国产亚洲夜色av网站| 鲁大师成人一区二区三区| 日韩精品久久久久久| 国产成人免费av一区二区午夜| 美女少妇全过程你懂的久久| 在线一区二区三区视频| 精品中文在线| 美女精品在线观看| 麻豆成人av在线| 亚洲精品一二三区区别| 91精品国产自产观看在线| 亚洲午夜天堂| 日韩欧美另类中文字幕| 国产精品久久久久蜜臀 | 久久中文字幕av一区二区不卡| 亚洲精品女人| 中文字幕在线免费观看视频| 亚洲毛片一区| 999国产精品| 国产精品一区三区在线观看| 欧美日韩尤物久久| 日韩中文字幕在线一区| 日韩在线中文| 欧美日韩网址| 久久成人亚洲| 成人小电影网站| 国产欧美大片| 视频精品一区二区| 美女性感视频久久| 免费一级片91| 午夜av成人| 久久亚洲道色| 日本成人在线一区| 悠悠资源网久久精品| 高清久久一区| 国产日韩1区| 手机精品视频在线观看| 韩国精品主播一区二区在线观看| 日韩和欧美一区二区三区| 亚洲不卡av不卡一区二区| 国产精品一国产精品| 99在线|亚洲一区二区| 国产理论在线| 欧美日韩18| 亚洲乱码久久| 在线视频日韩| 欧美不卡高清| 日本少妇一区| 日韩欧美精品| 国产在线不卡一区二区三区| 日韩中文字幕| 蜜臀av一区二区三区| 亚洲欧美日韩高清在线| av资源新版天堂在线| 国产麻豆精品| 日韩高清一区| 亚洲精品在线国产| 久久国产高清| 亚洲黑丝一区二区| 人在线成免费视频| 成人污污视频| 国产成人1区| 国产精品mv在线观看| 国产探花一区在线观看| 亚洲欧美网站在线观看| 蜜桃传媒麻豆第一区在线观看| 99pao成人国产永久免费视频 | 国产精品3区|