Iterator迭代删除的注意事项

1、Iterator介绍

Iterator 用同一种逻辑来遍历集合。使得客户端自身不需要来维护集合的内部结构,所有的内部状态都由Iterator来维护。客户端从不直接和集合类打交道,它总是控制Iterator,向它发送"向前","向后","取当前元素"的命令,就可以间接遍历整个集合。
在Java中Iterator为一个接口,它只提供了迭代了基本规则,在JDK中他是这样定义的:对 collection 进行迭代的迭代器。
接口定义为:
public interface Iterator{
     boolean hasNext();
     Object next(); 
     void remove();
}

 其中:

        Object next():返回迭代器刚越过的元素的引用,返回值是Object,需要强制转换成自己需要的类型

        boolean hasNext():判断容器内是否还有可供访问的元素

        void remove():删除迭代器刚越过的元素

2、ArrayList的Iterator实现。

    在 ArrayList 内部首先是定义一个内部类 Itr,该内部类实现 Iterator 接口,如下:
private class Itr implements Iterator<E> {
  //....
}
在内部类实现了Iterator接口,而ArrayList的Iterator是返回的它的内部类Itr,所以我们主要看看Itr是如何实现的。
public Iterator<E> iterator() {
  return new Itr();
}

private class Itr  implements Iterator<E> {
     int cursor;        //  下一个元素的索引位置
     int lastRet = - 1//  上一个元素的索引位置 ; -1 if no such
     int expectedModCount = modCount;  //修改的次数       modCount,记录了ArrayList结构性变化的次数

     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 (expectedModCount != ArrayList. this.modCount)
         throw new ConcurrentModificationException();
}
}

1、expectedModCount的初值为modCount

2、hasNext的判断条件为cursor!=size,就是当前迭代的位置不是数组的最大容量值就返回true

3、next和remove操作之前都会先调用checkForComodification来检查expectedModCount和modCount是否相等

  如果没checkForComodification去检查expectedModCount与modCount相等,这个程序肯定会报ArrayIndexOutOfBoundsException


3 测试Iterator

public static void main(String[] args) {
    List<String> stringList=  new ArrayList<String>();
    stringList.add( "a");
    stringList.add( "b");
    stringList.add( "c");
    stringList.add( "d");
    Iterator<String> it = stringList.iterator();
     while(it.hasNext()){
        String str = it.next();
         if(str.contains( "c")){
                stringList.remove(str);
        } else {
          logger.info( "str:==========={}",str);
       }
    }
}
输出结果为:
str:===========a
str:===========b
d并没有输出, 因为在删除 c 的时候cursor为3,size也变成了3。所以hasNext就返回为false了,循环结束,从而后面的元素也不会输出了.
将stringList.remove(str); 改为 it.remove()
输出结果为:
str:===========a
str:===========b
str:===========d 
因为iterator 的remove 方法,在删除当前元素的同时,将 cursor 置为了 2  并且将 expectedModCount  设为  modCount ; 维护了索引的一致性,而当前的size=3  所以 hasNext方法返回为true,循环继续,所以将d正常输出了。
整个过程 cursor、lastRet 、expectedModCount、size 的值变化为 (0 -1 4 4 ) ——>(1 0 4  4)输出a ——>  (2  1  4  4)输出b——> (3 2 4 4 )删除c ——>(2 -1  5  3 )——>(3 2 5 3 )输出d——>cursor=size 循环结束



猜你喜欢

转载自blog.csdn.net/qq_37012236/article/details/78962811
今日推荐