zoukankan      html  css  js  c++  java
  • 为什么不能在增强for中删除集合的元素

    学习的过程中遇到的一个问题, 下述代码在尝试remove其他元素的时候会出现异常,而在remove最后一个元素的时候,可以正常运行.

    public class Main {
        public static void main(String[] args) {
            TreeMap<ModelWorker, String> map = new TreeMap<>();
            map.put(new ModelWorker("张三", 18), "北京");
            map.put(new ModelWorker("李四", 20), "上海");
            map.put(new ModelWorker("王五", 35), "天津");
            
            for (ModelWorker modelWorker : set) {
                if ("张三".equals(modelWorker.getName())) {
                    map.remove(modelWorkermove(modelWorker)); // 这里尝试remove最后一个对象
                }
            }
        }
    }
    

    初步查找后得知,增强for和迭代器遍历的过程中,直接用集合去remove以及其他修改集合的操作很容易出现问题.
    之后Debug+翻源码分析了一下异常抛出的原因:
    异常是在TreeMap中的一个继承Iterator的抽象类中抛出的
    next()方法源码如下
    throw new ConcurrentModificationException();这行抛出异常原因显然是因为if条件满足modCount != expectedModCount 虽然不知道为什么jdk这样设计的.这两个变量在TreeMap类的成员变量中定义,没搞懂是干嘛的.先继续往下找找什么操作会导致这两个变量的变化.

    final Entry<K,V> nextEntry() {
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        next = successor(e);
        lastReturned = e;
        return e;
    }
    

    在remove()方法中发现调用了deleteEntry方法,该方法修改了导致异常抛出的条件的变量.
    remove()方法导致变量值修改的核心部分

    private void deleteEntry(Entry<K,V> p) {
        modCount++;
        size--;
        // 省略没用的
    }
    

    目前到这里,仅仅是大致上搞懂了为什么当使用集合的remove方法移除元素的时候会抛异常这点.还有一个问题有待解决,当移除最后一个元素的时候却没有抛出异常.
    回头检查自己的代码.增强for在编译后会变成迭代器.反编译后的代码如下.

        Iterator<ModelWorker> var3=set.iterator();
        while(var3.hasNext()){
        ModelWorker1 modelWorker=var3.next();
            if("张三".equals(modelWorker.getName())){
            map.remove(modelWorker);
            }
        }
    

    从这里猜测会不会和代码按顺序执行有关,当remove最后一个元素后,下一行要执行的是hasNext,当hasNext执行完后返回false后循环会直接结束,而不进入next方法.并且异常可能只能是next方法抛出的.
    所以顺便查了下hasNext的源码.在此之中果然没有抛出异常的语句.

    hasNext()方法的源码

    public final boolean hasNext() {
        return next != null;
    }
    

    最后得到的结论就是,不要在增强for和迭代器遍历中直接删除集合的元素.如果没抛异常可能就是因为删除的是最后一个元素.

  • 相关阅读:
    谷歌在外贸中的实战解析
    关于外贸淡季的几点看法
    如何打造完美报价单
    bbs
    你身上的所有光环,同样是套在你身上的枷锁
    Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一
    用C#生成随机中文汉字验证码的基本原理
    必须根据不同的数据内容来套用不同的编码或是译码函式,以便取得对应的字节或是字符串数据
    锁和多线程:5种锁介绍(三)
    锁和多线程:3种锁的使用(二)
  • 原文地址:https://www.cnblogs.com/yao-xi/p/13915597.html
Copyright © 2011-2022 走看看