• Java_foreach不能remove


    foreach

    阿里巴巴java开发手册

    【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

    反例
     List<String> a = new ArrayList<String>(); 
     a.add("1");
     a.add("2");
     for (String temp : a) { 
        if("1".equals(temp)){
             a.remove(temp); 
         } 
     } 
     正例
     Iterator<String> it= a.iterator(); 
    while(it.hasNext()){ 
        String temp = it.next(); 
        if(删除元素的条件){ 
            it.remove(); 
        } 
    }
    

    foreach源码

    foreach遍历集合,其实是走的Iterator,首先判断hasNext(),如果没有了则终止循环,否则next()获取元素时,next()时,都要check一下集合元素个数是否变化了,如果变化了,则抛出异常。

    Itr是ArrayList的内部类,实现了Iterator接口

    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;//游标不等于元素个数就是还有下一个
     }
    
    public E next() {
         checkForComodification();//check是否并发修改
          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();
      }
    }
    

    modCount是集合添加元素、删除元素的次数,expectedModCount是预期的修改次数。增删操作会使得modCount+1,不等于expetedModCount,所以抛出异常。

    没有使用list.iterator时调用的是ArrayList自己的remove,并不会同步这两个值,导致抛出异常。调用了ArrayList.iterator之后,然后了Itr对象,此后再remove,remove方法中有让这两个值相等的操作。

    迭代器方式移除

    那为什么Iterator不会异常呢?

    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();
        }
    }
    

    迭代器的remove方法会修改expectedModCount,从而使modCount与之相等

    参考:https://blog.csdn.net/wangjun5159/article/details/61415358

  • 相关阅读:
    今天再次认真整理了浏览器收藏夹
    今天再次认真整理了浏览器收藏夹
    【博客之星】CSDN2013博客之星--分析和预测
    【博客之星】CSDN2013博客之星--分析和预测
    CSDN博客的文章分类和战略规划
    CSDN博客的文章分类和战略规划
    《社交红利》读书总结--如何从微信微博QQ空间等社交网络带走海量用户、流量与收入
    《社交红利》读书总结--如何从微信微博QQ空间等社交网络带走海量用户、流量与收入
    个人名片与诚信
    个人名片与诚信
  • 原文地址:https://www.cnblogs.com/AganRun/p/11816053.html
Copyright © 2020-2023  润新知