• 遍历集合时删除集合中的元素问题


    使用迭代器Iterator遍历集合元素时,如果删除的元素不是倒数第二个数据,则会抛出ConcurrentModificationException异常

    public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("a");list.add("b");list.add("c");
            for (Iterator<String> it = list.iterator();it.hasNext();){
                String first = it.next();
                if ("a".equals(first)) {
                    list.remove(first);
                }
                System.out.println(first);
            }
        }

    以上代码运行结果如下图:

    原因:由于迭代器只负责对各种集合所包含的元素进行迭代,它自己并没有保留集合元素。它判断是否还有下一个元素的标准很简单:如果下一步即将访问的元素的索引不等于集合的大小,就会返回true,否则,返回false。当程序使用迭代器遍历集合的倒数第2个元素时,下一步即将访问的元素的索引为size()-1。如果此时通过List删除集合的任意一个元素,将导致集合size()变成size()-1,这将导致hasNext()方法返回false,也就是说遍历会提前结束,永远访问不到最后一个元素。迭代器在获取下一个元素的next()方法中,会调用checkForComodification()方法来检查集合是否被修改:遍历之前使用expectedModCount保留该集合被修改的次数,每次获取集合的下一个元素之前,检查集合的当前修改次数modCount与遍历之前的修改次数expectedModCount是否相等,如果不相等就直接抛出ConcurrentModification异常。源码如下

    hasNext()源码:

    public boolean hasNext() {
           return cursor != size;
    }

    next()方法源码:

          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];
            }
           // 如果修改了,直接抛异常
           final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }

    如果使用普通的for循环删除集合中元素,也会出现数据丢失问题:

     List<String> list = new ArrayList<>();
     list.add("a");list.add("b");list.add("c");
     for (int i = 0;i<list.size();i++) {
         String first = list.get(i);
         System.out.println(first);
         if ("a".equals(first)) {
             list.remove(first);
         }
     }
    // 输出结果为 a  c

    上述代码为什么丢失了元素b呢?原因是:删除元素a之后,集合中剩余的元素会“整体搬家”,所在的角标都会往前移动一位,此时元素b的角标由1变成了0,元素c的角标由2变成了1,而变量i此时的值为1,因此访问时丢失了元素b

    使用增强版的for循环会出现和Iterator遍历时相同的问题

            List<String> list = new ArrayList<>();
            list.add("a");list.add("b");list.add("c");
            for (String value:list) {
                System.out.println(value);
                if ("a".equals(value)) {
                    list.remove(value);
                }
            }    

    如果想删除集合中的某些元素,可以先定义一个变量,在遍历集合时,将需要删除的元素都添加到该变量中,在循环外统一删除。

            List<String> list = new ArrayList<>();
            list.add("a");list.add("b");list.add("c");
            List<String> deleteList = new ArrayList<>();
            for (String value:list) {
                if ("a".equals(value)) {
                    deleteList.add(value);
                }
            }
            if (deleteList.size() > 0) {
                list.removeAll(deleteList);
                deleteList = null;
            }
            // 输出结果为  b  c 
            for (String value:list) {
                System.out.println(value);
            }
  • 相关阅读:
    jaxb解析xml工具类
    JQuery的父、子、兄弟节点查找方法
    jw player 配置参数
    jQuery判断当前元素是第几个元素&获取第N个元素
    正则表达式中test,match,exec区别
    php常用函数file
    php常用函数time
    php常用array函数
    php常用string函数
    Linux常用命令
  • 原文地址:https://www.cnblogs.com/zhlblogs/p/13595042.html
Copyright © 2020-2023  润新知