java迭代器移除元素出现并发修改异常的原因及解决

 更新时间:2020年12月03日 09:51:26   作者:NGUhuang  
这篇文章主要介绍了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 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 ,也无权访问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迭代器移除元素出现并发修改异常的原因及解决就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java 中普通代码块,构造代码块,静态代码块区别及代码示例

    Java 中普通代码块,构造代码块,静态代码块区别及代码示例

    这篇文章主要介绍了Java 中普通代码块,构造代码块,静态代码块区别及代码示例的相关资料,需要的朋友可以参考下
    2017-01-01
  • Java中volatile防止指令重排

    Java中volatile防止指令重排

    volatile可以防止指令重排,在多线程环境下有时候我们需要使用volatile来防止指令重排,来保证代码运行后数据的准确性,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Java 普通代码块静态代码块执行顺序(实例讲解)

    Java 普通代码块静态代码块执行顺序(实例讲解)

    下面小编就为大家带来一篇Java 普通代码块静态代码块执行顺序(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • Java并发编程之同步容器与并发容器详解

    Java并发编程之同步容器与并发容器详解

    今天给大家带来的文章是Java并发编程的相关知识,文中对java同步容器与并发容器做了非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • Java设计模式之建造者模式

    Java设计模式之建造者模式

    这篇文章介绍了Java设计模式之建造者模式,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • 详解Spring Boot中整合Sharding-JDBC读写分离示例

    详解Spring Boot中整合Sharding-JDBC读写分离示例

    这篇文章主要介绍了详解Spring Boot中整合Sharding-JDBC读写分离示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-03-03
  • SpringBoot中的CSRF攻击及预防方法

    SpringBoot中的CSRF攻击及预防方法

    CSRF攻击是一种常见的网络攻击方式,可以通过欺骗用户来执行恶意操作,在Spring Boot应用程序中,我们可以采取多种措施来预防CSRF攻击,本文将给大家介绍一下CSRF攻击以及如何预防攻击,需要的朋友可以参考下
    2023-07-07
  • SpringAOP实现日志收集管理功能(步骤详解)

    SpringAOP实现日志收集管理功能(步骤详解)

    这篇文章主要介绍了SpringAOP实现日志收集管理功能,本文分步骤通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • Java enum关键字不识别的快速解决办法

    Java enum关键字不识别的快速解决办法

    这篇文章主要介绍了Java enum关键字不识别的快速解决办法,非常不错,具有参考借鉴价值,感兴趣的朋友一起看看吧
    2016-09-09
  • Java 正则表达式详细介绍

    Java 正则表达式详细介绍

    本文主要介绍 Java 正则表达式的内容,这里整理了Java 正则表达式的相关资料,并详细介绍,附有代码示例,有兴趣的小伙伴可以参考下
    2016-09-09

最新评论