并发修改异常 && 迭代器

一、并发修改异常

对集合进行遍历时,对集合进行增、删操作,会引起并发修改异常。

方案一:使用迭代器

foreach底层也是使用Iterator迭代器进行迭代的

    public static void main(String[] args) {
        ArrayList<Integer> arrayLists = new ArrayList<>();
        arrayLists.add(new Integer(1));
        arrayLists.add(new Integer(2));
        arrayLists.add(new Integer(3));
        arrayLists.add(new Integer(4));
        arrayLists.add(new Integer(5));
        arrayLists.add(new Integer(6));
        arrayLists.add(new Integer(7));
        arrayLists.add(new Integer(8));

        Iterator<Integer> iterator = arrayLists.iterator();
        while(iterator.hasNext()){ 
            Integer num = iterator.next();
            if(num.equals(new Integer(3))){
                iterator.remove();  //删除操作
            }
        }

    }
    
方案二:扩容处理

需要remove/ add 的项集存入到另一集合中,在集合遍历结束后,再删除/添加 这些项集。

二、迭代器的介绍

迭代器是一个对象,他的工作是遍历并选择序列中的对象。此外,迭代器通常被称为轻量级对象, 正因为它的创建代价小。Java中的Iterator只能单向移动,这个Iterator只能用来

  1. 使用方法iterator()要求容器放回一个Iterator,Iterator将准备好返回序列的第一个元素。
  2. 使用next()获得序列中的下一个元素。
  3. 使用hasNext()检查序列中是否还有元素(通常作为判断条件)。
  4. 使用remove()将迭代器新近返回的元素删除

这里要注意一点:迭代器并不是独立的一个对象,事实上它是操作容器的工具,通过迭代器的方法(比如remove)是直接影响到原容器的。

三、迭代器的介绍

  • 为何迭代器的代价比较小?
    因为迭代器不是一次性产生所有元素,下一个元素的创建要经过一次迭代,也就是调用一次next函数。

  • 容器可以通过调用iterator函数来创建一个对应类型的iterator对象,换句话说iterator对象可以接受任何容器调用iterator函数对它的初始化。这也就解释了为何迭代器可以同一不同类型容器的代码重用问题。

原文链接:https://blog.csdn.net/ztlzlzl/article/details/106987229

https://blog.csdn.net/qq_37935670/article/details/79583339

四、算法中的实际应用

函数功能:在候选项集中找到结果集

【版本1】利用2个迭代器进行遍历 (并发修改异常)

 //【版本1】利用2个迭代器进行遍历 (并发修改异常)
        //第一层循环,iterator遍历 x
        Iterator<ArrayList<Integer>> iterator = s_psfui.keySet().iterator();
        while(iterator.hasNext()){
            ArrayList<Integer> x = iterator.next();
            ULStructure x_list = s_psfui.get(x);
            //第二层循环,iterator遍历 y
            Iterator<ArrayList<Integer>> iterator2 = s_psfui.keySet().iterator();
            while(iterator2.hasNext()){
                ArrayList<Integer> y = iterator2.next();
                ULStructure y_list = s_psfui.get(y);
                if((x_list.size() >= y_list.size() && x_list.utilSum() > y_list.utilSum()) || (x_list.size() > y_list.size() && x_list.utilSum() >= y_list.utilSum())){
                    iterator2.remove();  //删除y
                }
            }
        }

【版本2】for + 迭代器进行遍历 (报并发修改异常)

        //【版本2】for + 迭代器进行遍历  (报并发修改异常)
        //第一层循环,iterator遍历 x
        Iterator<ArrayList<Integer>> iterator = s_psfui.keySet().iterator();
        while(iterator.hasNext()){
            ArrayList<Integer> x = iterator.next();
            ULStructure x_list = s_psfui.get(x);
            //删除的项集
            ArrayList<ArrayList<Integer>> remove_set = new ArrayList<>();
            //第二层循环,iterator遍历 y
            for(ArrayList<Integer> y : s_psfui.keySet()){
                ULStructure y_list = s_psfui.get(y);
                if((x_list.size() >= y_list.size() && x_list.utilSum() > y_list.utilSum()) || (x_list.size() > y_list.size() && x_list.utilSum() >= y_list.utilSum())){
                    remove_set.add(y); //删除y
                }
            }
            for(ArrayList<Integer> itemset : remove_set){
                s_psfui.remove(itemset);
            }
        }

即使不在for()循环中做删除,也不行!

【小测试】 iterator1 & iterator2 两个迭代器是否相互影响

        //【小测试】 iterator1 & iterator2 两个迭代器是否相互影响(iterator2修改后,iterator1是否知道增删。回答:不知道)
        Iterator<ArrayList<Integer>> iterator1 = s_psfui.keySet().iterator();
        Iterator<ArrayList<Integer>> iterator2 = s_psfui.keySet().iterator();

        iterator2.next();
        if(iterator2.hasNext())
        iterator2.remove();
        System.out.println(iterator1.hasNext());//true
        iterator1.next(); //实际运行结果,这里抛出并发修改异常,说明1的信息与2不同步(iterator2.remove()后,iterator1 找不到下一个元素)
        System.out.println(iterator1.hasNext());//false--》如果联动,那么二号元素已经被删除,输出false

        //最终结论:iterator只能删除迭代器当前指向的元素,否则可能会抛出并发修改异常

【版本3】两层for()循环,将删除元素放到集合中,遍历结束后再删除

 //【版本3】两层for()循环,将删除元素放到集合中,遍历结束后再删除
        //第一层循环,iterator遍历 x
        Set<ArrayList<Integer>> remove_set=new HashSet<>();
        for(ArrayList<Integer> x : s_psfui.keySet()) {
            if (!remove_set.contains(x)) {
                ULStructure x_list = s_psfui.get(x);
                //第二层循环,iterator遍历 y
                for (ArrayList<Integer> y : s_psfui.keySet()) {
                    if(!remove_set.contains(y)) {
                        ULStructure y_list = s_psfui.get(y);
                        if ((x_list.size() >= y_list.size() && x_list.utilSum() > y_list.utilSum()) || (x_list.size() > y_list.size() && x_list.utilSum() >= y_list.utilSum())) {
                            remove_set.add(y); //将需要删除元素加入到集合中
                        }
                    }
                }
            }
        }
        //删除被支配的项集
        for (ArrayList<Integer> integerArrayList : remove_set) {
            s_psfui.remove(integerArrayList);
        }
        remove_set.clear();
几个改进的点:

版本3需要一个额外的空间remove_set用于存储待删除的项集,但remove_set最大空间占用只是跟原集合s_psfui一样,是一个2倍的空间开销。扩容实现需要较小的内存开销,在线性范围内,可以接受。(并且避免了使用迭代器,每次用迭代器容易出奇奇怪怪的问题)

1. 在remove_set使用完毕后,进行remove_set.clear(),及时释放内存。
2. 外层可以用迭代器进行遍历,当判断到remove_set.contains(x),可以顺势将X在迭代器中进行删除。(不知道是否可行,可以试试)
3. 需要注意for()循环层中,需要进行remove_set.contains()的操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值