• foreach & iterator 遍历修改容器出错, Mutation undermines an iterator


    问题引入:

    在一次实验作业中,出现了这样的bug:

    对于有向加权图graph的边集合(已保存 a->b , b->c 两条边),通过for-each循环删除 含有节点b的边:a->b(b作为target),b->c(b作为source),根据逻辑,循环结束,graph为空。

    代码如下:

     1     public boolean remove(String vertex) {
     2         for (Edge edge : edges) {
     3             System.out.println(toString());
     4             System.out.println();
     5             if (edge.containSource(vertex)) { // as source
     6                 edges.remove(edge);
     7             }
     8             if (edge.containTarget(vertex)) { // as target
     9                 edges.remove(edge);
    10             }
    11         }
    12 
    13         System.out.println("循环外"+toString()); // 显示循环后的graph
    14         System.out.println();
    15         vertices.remove(vertex);
    16 //        checkRep();
    17         return true;
    18     }

    运行结果如下:

    b -> c : 2
    a -> b : 3
    
    
    循环外a -> b : 3

      graph中有两个edge,应当进行两次循环,但实际只输出了第一次进入循环的状态。而循环结束后graph为非空,仍含有一条含b节点的边。

    问题分析:

    用一个段简单的代码模拟上述过程:(遍历set,删除元素“b”,print其他元素)

     1         Set<String> set = new HashSet<>();
     2         set.add("a");
     3         set.add("b");
     4         set.add("c");
     5         for (String string : set) {
     6             if (string.equals("b")) {
     7                 set.remove(string);
     8             }else {
     9                 System.out.println(string);
    10             }
    11         }

    运行结果:

    a
    Exception in thread "main" java.util.ConcurrentModificationException
        at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1494)
        at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1517)
        at test.main(test.java:27)

    exception!当多个线程对Collection进行操作时,若其中某一个线程遍历集合时,该集合的内容被其他线程所改变,则会抛出ConcurrentModificationException异常。

    分析:为保证在遍历集合时不出错误,就应该保证便利过程中不对集合产生结构上的修改。

    经验证,原问题也系ConcurrentModification,但没有thorw exception ,原因尚不明确,以后讨论。

    问题解决:

    Java迭代器:

    迭代器模式:就是提供一种方法对一个容器对象中的各个元素进行访问,而又不暴露该对象容器的内部细节。

    概述

      Java集合框架的集合类,我们有时候称之为容器。容器的种类有很多种,比如ArrayList、LinkedList、HashSet...,每种容器都有自己的特点,ArrayList底层维护的是一个数组;LinkedList是链表结构的;HashSet依赖的是哈希表,每种容器都有自己特有的数据结构。

      因为容器的内部结构不同,很多时候可能不知道该怎样去遍历一个容器中的元素。所以为了使对容器内元素的操作更为简单,Java引入了迭代器模式! 

      把访问逻辑从不同类型的集合类中抽取出来,从而避免向外部暴露集合的内部结构。

    迭代器的特性使得迭代器可以在迭代期间从集合中移除元素。

    问题引入中的删除节点函数修改后如下:

     1     public boolean remove(String vertex) {
     2         Iterator<Edge> iter = edges.iterator();
     3         while (iter.hasNext()) {
     4             Edge edge = (Edge) iter.next();
     5             if (edge.containSource(vertex)) { // as source
     6                 iter.remove();
     7             }
     8             else if (edge.containTarget(vertex)) { // as target
     9                 iter.remove();
    10             }
    11         }
    12         
    13         checkRep();
    14         return true;
    15     }

    使用迭代器iteratorremove方法实现修改集合操作。

    在循环体内先print graph,在循环外print graph 写过如下:循环体内先后删除含b节点的边,循环体外无边。

    b -> c : 2
    a -> b : 3
    
    
    a -> b : 3
    
    
    循环外

    不过目前尚不清楚为何源代码没有thorw exception ConcurrentModificationException,希望有了解的朋友可以提示下!

  • 相关阅读:
    遇到的开发错误
    我的麦本本配置
    C#:100以内能被7整除的最大自然数
    C#:静态字段和静态方法的学习
    Oracle 备份、恢复单表或多表数据步骤 (转)
    有关关键路径的概念和算法 (转)
    Delphi中StringReplace函数的使用
    Delphi 里 FillChar的用法
    Delphi中destroy, free, freeAndNil, release用法和区别
    项目经理、系统架构师或技术骨干应该具备的水平
  • 原文地址:https://www.cnblogs.com/standingby/p/8623714.html
Copyright © 2020-2023  润新知