zoukankan      html  css  js  c++  java
  • 不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁

    下图是来自阿里巴巴java开发手册,里面的例子有些特殊,在后面会有一般的例子,请往后看。

    个人理解:在foreach循环里面进行元素的remove/add操作就是:在foreach循环里使用集合本身的remove/add方法, Iterator方式就是使用集合对应的iterator的remove/add方法,即下图中的iterator.remove()  //删除当前遍历的这个元素

     foreach本质上是java中的语法糖,对数组操作时,底层原理和普通的for循环一样;而对列表操作时,实际上是采用了迭代器的方法。

    来源于阿里巴巴java开发手册

    为什么不要使用集合本身的remove/add方法呢?

    List<String> list = new ArrayList<String>();
    list.add("1");
    list.add("2");
    for (String temp : list) {
         if("1".equals(temp)){
             list.remove(temp);
       } 
    }

    反编译结果如下:

    List list = new ArrayList();
    list.add("1");
    list.add("2");
    Iterator i$ = list.iterator();     // 语句1
    do
    {
        if(!i$.hasNext())           
             break;
        String temp = (String)i$.next(); // 语句2 
      if("1".equals(temp)) list.remove(temp); // 语句3 
    }
    while(true);

    ArrayList的iterator()方法如下:

    重要参数:

    modCount:是ArrayList修改的次数,在add()、remove()/removeAll()、clear()等方法中会修改modCount的值。

    expectedModCount:在获得ArrayList对象的Iterator的时候,把modCount的初值赋给expectedModCount

    语句1: list.iterator() 返回的是一个Itr对象,

    语句2: next()方法中,首先会执行checkForComodification()方法(源码如下),如果 modCount != expectedModCount 就会抛出ConcurrentModificationException异常,在语句3中,执行了list.remove(temp),会修改modCout的值,那么在下一次执行next()方法的时候就会报错!! 所以不要在foreach循环里面进行元素的remove/add操作

    另:如果把上面代码  if("1".equals(temp)) 改成  if("2".equals(temp)) 即直接删除最后一个元素,会抛出ConcurrentModificationException异常,因为在删除最后一个元素的过程中,首先cursor 等于 size, 然后执行 a.remove(temp)  【-》modCount发生改变】方法后,size=size-1, 再执行hashNext()方法,发现返回结果是false(cursor != size) 又会进行一次循环,在next()方法里面就抛出异常。 所以不要在foreach循环里面进行元素的remove/add操作

    public Iterator<E> iterator() {
        return new Itr();
    }
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;
    
        public boolean hasNext() {
            return cursor != size;
        }
    
        @SuppressWarnings("unchecked")
        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];
        }
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
    
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

    为什么说上面的例子有些特殊,因为list集合中只有两个数据,如果是只删除第一个数据,执行list.remove(temp)后, cursor=size-1,  size=size-1, 所以就会推出循环。没有抛出异常。

    将例子改为:

    List<String> list = new ArrayList<String>();
    list.add("1");
    list.add("2");
    list.add("3");
    for (String temp : list) {
         if("1".equals(temp)){
             list.remove(temp);
       } 
    }

    现在有了三个元素,除开直接删除倒数第二个元素不会抛出异常(因为会出现  cursor=size-1,  size=size-1的情况,然后退出循环),直接删除其他元素都会抛出ConcurrentModificationException异常!!

    如果是下面的写法,更是会抛出异常!

    List<String> list = new ArrayList<String>();
    list.add("1");
    list.add("2");
    list.add("3");
    for (String temp : list) {
         list.remove(temp);
    }

    直接使用iterator的remove/add方法对集合做修改:

    List<String> list = new ArrayList<>();      
    list.add("1");      
    list.add("2");
    list.add("3");
    list.add("4");
    Iterator iterator = list.iterator();
    while(iterator.hasNext()) {
        String temp = (String) iterator.next();
        iterator.remove();
    }
  • 相关阅读:
    fedora上部署ASP.NET——(卡带式电脑跑.NET WEB服务器)
    SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志
    8086CPU的出栈(pop)和入栈(push) 都是以字为单位进行的
    FTP 服务搭建后不能访问问题解决
    指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
    Linux 安装MongoDB 并设置防火墙,使用远程客户端访问
    svn Please execute the 'Cleanup' command. 问题解决
    .net 操作MongoDB 基础
    oracle 使用绑定变量极大的提升性能
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。
  • 原文地址:https://www.cnblogs.com/DDiamondd/p/11307825.html
Copyright © 2011-2022 走看看