java迭代器移除元素出現并發修改異常的原因及解決
迭代器(Iterator的對象)主要用于遍歷集合,體現的就是迭代器模式。
Iterator接口定義了以下四種方法。
boolean hasNext():如果集合還沒遍歷完就返回true。
Object next():返回集合里的下一個元素。
void remove():刪除集合里上一次next方法返回的元素。
void forEachRemaining(Consumer action):這是java8新增的默認方法,可用Lambda表達式遍歷數組。
使用迭代器遍歷元素時不能不能通過Collection接口中的remove方法刪除元素,只能用Interator的remove方法刪除元素,下面根據案例和源代碼分析原因。
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并發修改異常 System.out.println(str); if('lisi'.equals(str)) { list.remove(str); } } System.out.println(list); }}
并發修改異常: 當方法檢測到對象的并發修改,但不允許這種修改時,拋出此異常。例如,某個線程在 Collection 上進行迭代時,通常不允許另一個線性修改該 Collection(來自java API),從這里可以看出迭代器和集合是在不同線程里的。
查看資料知道了,迭代器其實在另外一個線程復制了一個一摸一樣的集合進行遍歷的。當用集合的remove方法刪除元素時,迭代器是不會知道的,所以就會拋出異常。
下面看源碼分析。
public E next() { checkForComodification();//檢查迭代器元素修改的次數是否和集合元素修改的此處一樣,如果不一樣則會拋出并發修改異常 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方法刪除元素時,實際在底層還是用的集合的remove方法,所以迭代器和集合修改元素的次數一樣是不會出現異常的。
源碼如下:
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(); } }
并發修改異常出現的原因已經找到了。但是Arraylist迭代器會出現下面這種情況,當我們用集合刪除方法刪除倒數第二個元素時,并不會出現異常。
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();//不會出現并發修改異常 System.out.println(str); if('wangwu'.equals(str)) {//用集合Remove方法刪除倒數第二個元素 list.remove(str); } } System.out.println(list); }}
原因是這樣的,當while循環到第三次的時候也就是遍歷到“wangwu”時,這時候迭代器的cursor(游標相當于指針)變量值為2,集合元素個數為4。執行完it.next()方法后cursor值為3,接著刪除“wangwu”這個元素后,集合的size變成了3。當繼續第四次循環時現判斷hasNext()當cursor值和size值相等時返回false,所以不會執行while循環里面的語句,自然不會執行next()方法,所以時不會出現異常的。
public boolean hasNext() { return cursor != size;//根據上面的說法在循環第四次是返回的是false,不會執行循環里的的代碼 }
補充知識:java使用迭代器刪除元素_使用Java從地圖中刪除元素
關于從Java中的Map刪除元素的非常簡短的文章。 我們將專注于刪除多個元素,而忽略了您可以使用Map.remove刪除單個元素的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');
有幾種刪除元素的方法。 您可以手動遍歷代碼并將其刪除:
for(Iterator<Integer> iterator = map.keySet().iterator(); iterator.hasNext(); ) { Integer key = iterator.next(); if(key != 1) { iterator.remove(); }}
這是您無需訪問Java 8+即可執行的操作。 從Map刪除元素時,需要Iterator來防止ConcurrentModificationException 。
如果您確實有權使用Java(8+)的較新版本,則可以從以下選項中進行選擇:
// remove by valuemap.values().removeIf(value -> !value.contains('1'));// remove by keymap.keySet().removeIf(key -> key != 1);// remove by entry / combination of key + valuemap.entrySet().removeIf(entry -> entry.getKey() != 1);
removeIf是Collection可用的方法。 是的, Map本身不是Collection ,也無權訪問removeIf本身。
但是,通過使用: values , keySet或entrySet ,將返回Map內容的視圖。 該視圖實現Collection允許在其上調用removeIf 。
由values , keySet和entrySet返回的內容非常重要。
以下是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都會改變另一個。 我認為我無法解釋JavaDoc所說的內容,而不是那里已經寫的內容。因此,我現在將不再嘗試該部分。 我只顯示了values的文檔,但是當我說keySet和entrySet也都由Map的內容作為后盾時,您可以信任我。 如果您不相信我,可以自己閱讀文檔。
這也使用舊版 Java版本鏈接回第一個示例。 該文檔指定可以使用Iterator.remove 。 這是早先使用的。 此外, removeIf的實現與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;}
還有一些額外的東西。 但是,否則幾乎是相同的。
就是這樣。 除了讓我記住要告訴您的記住以外,沒有太多結論了:使用values , keySet或entrySet將提供對removeIf訪問,從而允許輕松刪除Map條目。
以上這篇java迭代器移除元素出現并發修改異常的原因及解決就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持好吧啦網。
相關文章: