java迭代器移除元素出現(xiàn)并發(fā)修改異常的原因及解決
迭代器(Iterator的對(duì)象)主要用于遍歷集合,體現(xiàn)的就是迭代器模式。
Iterator接口定義了以下四種方法。
boolean hasNext():如果集合還沒遍歷完就返回true。
Object next():返回集合里的下一個(gè)元素。
void remove():刪除集合里上一次next方法返回的元素。
void forEachRemaining(Consumer action):這是java8新增的默認(rèn)方法,可用Lambda表達(dá)式遍歷數(shù)組。
使用迭代器遍歷元素時(shí)不能不能通過Collection接口中的remove方法刪除元素,只能用Interator的remove方法刪除元素,下面根據(jù)案例和源代碼分析原因。
public class InteratorTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String str = it.next();//java.util.ConcurrentModificationException并發(fā)修改異常
System.out.println(str);
if("lisi".equals(str)) {
list.remove(str);
}
}
System.out.println(list);
}
}
并發(fā)修改異常: 當(dāng)方法檢測(cè)到對(duì)象的并發(fā)修改,但不允許這種修改時(shí),拋出此異常。例如,某個(gè)線程在 Collection 上進(jìn)行迭代時(shí),通常不允許另一個(gè)線性修改該 Collection(來自java API),從這里可以看出迭代器和集合是在不同線程里的。
查看資料知道了,迭代器其實(shí)在另外一個(gè)線程復(fù)制了一個(gè)一摸一樣的集合進(jìn)行遍歷的。當(dāng)用集合的remove方法刪除元素時(shí),迭代器是不會(huì)知道的,所以就會(huì)拋出異常。
下面看源碼分析。
public E next() {
checkForComodification();//檢查迭代器元素修改的次數(shù)是否和集合元素修改的此處一樣,如果不一樣則會(huì)拋出并發(fā)修改異常
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
而用迭代器的ramove方法刪除元素時(shí),實(shí)際在底層還是用的集合的remove方法,所以迭代器和集合修改元素的次數(shù)一樣是不會(huì)出現(xiàn)異常的。
源碼如下:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);//用的還是集合的remove方法
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
并發(fā)修改異常出現(xiàn)的原因已經(jīng)找到了。但是Arraylist迭代器會(huì)出現(xiàn)下面這種情況,當(dāng)我們用集合刪除方法刪除倒數(shù)第二個(gè)元素時(shí),并不會(huì)出現(xiàn)異常。
public class InteratorTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String str = it.next();//不會(huì)出現(xiàn)并發(fā)修改異常
System.out.println(str);
if("wangwu".equals(str)) {//用集合Remove方法刪除倒數(shù)第二個(gè)元素
list.remove(str);
}
}
System.out.println(list);
}
}
原因是這樣的,當(dāng)while循環(huán)到第三次的時(shí)候也就是遍歷到“wangwu”時(shí),這時(shí)候迭代器的cursor(游標(biāo)相當(dāng)于指針)變量值為2,集合元素個(gè)數(shù)為4。執(zhí)行完it.next()方法后cursor值為3,接著刪除“wangwu”這個(gè)元素后,集合的size變成了3。當(dāng)繼續(xù)第四次循環(huán)時(shí)現(xiàn)判斷hasNext()當(dāng)cursor值和size值相等時(shí)返回false,所以不會(huì)執(zhí)行while循環(huán)里面的語句,自然不會(huì)執(zhí)行next()方法,所以時(shí)不會(huì)出現(xiàn)異常的。
public boolean hasNext() {
return cursor != size;//根據(jù)上面的說法在循環(huán)第四次是返回的是false,不會(huì)執(zhí)行循環(huán)里的的代碼
}
補(bǔ)充知識(shí):java使用迭代器刪除元素_使用Java從地圖中刪除元素
關(guān)于從Java中的Map刪除元素的非常簡(jiǎn)短的文章。 我們將專注于刪除多個(gè)元素,而忽略了您可以使用Map.remove刪除單個(gè)元素的Map.remove 。
以下Map將用于此帖子:
Map<Integer, String> map = new HashMap<>(); map.put(1, "value 1"); map.put(2, "value 2"); map.put(3, "value 3"); map.put(4, "value 4"); map.put(5, "value 5");
有幾種刪除元素的方法。 您可以手動(dòng)遍歷代碼并將其刪除:
for(Iterator<Integer> iterator = map.keySet().iterator(); iterator.hasNext(); ) {
Integer key = iterator.next();
if(key != 1) {
iterator.remove();
}
}
這是您無需訪問Java 8+即可執(zhí)行的操作。 從Map刪除元素時(shí),需要Iterator來防止ConcurrentModificationException 。
如果您確實(shí)有權(quán)使用Java(8+)的較新版本,則可以從以下選項(xiàng)中進(jìn)行選擇:
// remove by value
map.values().removeIf(value -> !value.contains("1"));
// remove by key
map.keySet().removeIf(key -> key != 1);
// remove by entry / combination of key + value
map.entrySet().removeIf(entry -> entry.getKey() != 1);
removeIf是Collection可用的方法。 是的, Map本身不是Collection ,也無權(quán)訪問removeIf本身。
但是,通過使用: values , keySet或entrySet ,將返回Map內(nèi)容的視圖。 該視圖實(shí)現(xiàn)Collection允許在其上調(diào)用removeIf 。
由values , keySet和entrySet返回的內(nèi)容非常重要。
以下是JavaDoc的values摘錄:
* Returns a { this map. Collection} view of the values contained in * Returns a { @link Collection} view of the values contained in map. * The collection is backed by the map, so changes to the map are * reflected in the collection, and vice-versa. * * The collection supports element removal, which removes the corresponding * mapping from the map, via the { @code Iterator.remove}, * mapping from the map, via the { Iterator.remove}, * { @code Collection.remove}, { @code removeAll}, * { @code retainAll} and { @code clear} operations.
此JavaDoc解釋說,由values返回的Collection由Map支持,并且更改Collection或Map都會(huì)改變另一個(gè)。 我認(rèn)為我無法解釋JavaDoc所說的內(nèi)容,而不是那里已經(jīng)寫的內(nèi)容。因此,我現(xiàn)在將不再嘗試該部分。 我只顯示了values的文檔,但是當(dāng)我說keySet和entrySet也都由Map的內(nèi)容作為后盾時(shí),您可以信任我。 如果您不相信我,可以自己閱讀文檔。
這也使用舊版 Java版本鏈接回第一個(gè)示例。 該文檔指定可以使用Iterator.remove 。 這是早先使用的。 此外, removeIf的實(shí)現(xiàn)與Iterator示例非常相似。 討論完之后,我不妨展示一下:
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
還有一些額外的東西。 但是,否則幾乎是相同的。
就是這樣。 除了讓我記住要告訴您的記住以外,沒有太多結(jié)論了:使用values , keySet或entrySet將提供對(duì)removeIf訪問,從而允許輕松刪除Map條目。
以上這篇java迭代器移除元素出現(xiàn)并發(fā)修改異常的原因及解決就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
基于注解的springboot+mybatis的多數(shù)據(jù)源組件的實(shí)現(xiàn)代碼
這篇文章主要介紹了基于注解的springboot+mybatis的多數(shù)據(jù)源組件的實(shí)現(xiàn),會(huì)使用到多個(gè)數(shù)據(jù)源,文中通過代碼講解的非常詳細(xì),需要的朋友可以參考下2021-04-04
Java在Excel中添加水印的實(shí)現(xiàn)(單一水印、平鋪水印)
這篇文章主要介紹了Java在Excel中添加水印的實(shí)現(xiàn)(單一水印、平鋪水印),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Java中BigDecimal與0比較的一個(gè)坑實(shí)戰(zhàn)記錄
BigDecimal屬于大數(shù)據(jù),精度極高,不屬于基本數(shù)據(jù)類型,屬于java對(duì)象,下面這篇文章主要給大家介紹了關(guān)于Java中BigDecimal與0比較的一個(gè)坑的相關(guān)資料,需要的朋友可以參考下2022-12-12
Mybatis-Plus實(shí)現(xiàn)只更新部分字段的數(shù)據(jù)
這篇文章主要介紹了Mybatis-Plus實(shí)現(xiàn)只更新部分字段的數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
Java實(shí)現(xiàn)將容器 Map中的內(nèi)容保存到數(shù)組
這篇文章主要介紹了Java實(shí)現(xiàn)將容器 Map中的內(nèi)容保存到數(shù)組,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
idea springboot 修改css,jsp不重啟實(shí)現(xiàn)頁面更新的問題
這篇文章主要介紹了idea springboot 修改css,jsp不重啟實(shí)現(xiàn)頁面更新的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10

