一、什么是迭代器?
迭代器(Iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址,简单地说,迭代器是一种检查容器内元素并遍历元素的可带泛型数据类型。
Iterator提供了统一遍历操作集合元素的统一接口, Collection接口实现Iterable接口, 每个集合都通过实现Iterable接口中iterator()方法返回Iterator接口的实例, 然后对集合的元素进行迭代操作. 有一点需要注意的是:在迭代元素的时候不能通过集合的方法删除元素, 否则会抛出ConcurrentModificationException 异常. 但是可以通过Iterator接口中的remove()方法进行删除.
public interface Iterator<E> { }
二、迭代器(Iterator)的方法
(1)boolean hasNext():该方法用于判断列表中是否还有元素,返回true表示列表中还有元素。
(2)E next():该方法返回迭代中的下一个元素。
(3)default void remove():从底层集合中删除此迭代器返回的最后一个元素(可选操作)。 此方法只能调用一次next() 。
如果底层集合在迭代过程中以任何方式进行修改而不是通过调用此方法,则迭代器的行为是未指定的。
异常:
如果 remove操作不会被这个迭代器支持,则抛出UnsupportedOperationException异常;
如果 next方法尚未被调用,或者 remove方法在上次调用 next方法之后已经被调用,则抛出IllegalStateException异常;
(4)default void forEachRemaining(Consumer<? super E> action):对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常。
如果指定了该顺序,则按迭代的顺序执行操作。 动作抛出的异常被转发给呼叫者。
参数action:要为每个元素执行的操作。
import java.util.Iterator; import java.util.ListIterator; import java.util.concurrent.CopyOnWriteArrayList; /** * @ClassName: CopyOnWriterArrayListDemo * @Description: 使用迭代器遍历列表 * @Author: liuhefei * @Date: 2020/3/21 * @blog: https://www.imooc.com/u/1323320/articles **/ public class CopyOnWriteArrayListDemo { public static void main(String[] args) { CopyOnWriteArrayList<String> arrayList = new CopyOnWriteArrayList<>(); arrayList.add("深圳"); arrayList.add("上海"); arrayList.add("北京"); arrayList.add("南京"); arrayList.add("昆明"); //Iterator迭代器 Iterator<String> iter = arrayList.iterator(); while (((Iterator) iter).hasNext()){ System.out.println(((Iterator) iter).next()); } } }
三、什么是迭代器的弱一致性
所谓弱一致性就是指返回迭代器后,其他线程对list的增删改操作对迭代器是不可见的。
我们用一个例子来演示多线程下迭代器的弱一致性问题
import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; /** * @ClassName: CopyOnWriteArrayListDemo3 * @Description: 多线程下迭代器的弱一致性 * @Author: liuhefei * @Date: 2020/3/21 * @blog: https://www.imooc.com/u/1323320/articles **/ public class CopyOnWriteArrayListDemo3 { private static volatile CopyOnWriteArrayList<String> arrayList = new CopyOnWriteArrayList<>(); public static void main(String[] args) throws InterruptedException { arrayList.add("深圳"); arrayList.add("上海"); arrayList.add("北京"); arrayList.add("南京"); arrayList.add("昆明"); arrayList.add("大理"); arrayList.add("银川"); Thread thread = new Thread(new Runnable() { @Override public void run() { //修改arrayList中下标为2的元素 arrayList.set(2, "武汉"); //删除元素 arrayList.remove(4); arrayList.remove(5); } }); //保证在修改线程启动之前获取迭代器内容 Iterator<String> iter = arrayList.iterator(); //启动线程 thread.start(); //等待子线程执行完毕 thread.join(); //迭代列表 while (iter.hasNext()){ System.out.println(iter.next()); } } }
从代码中可以看出,main函数中先向arrayList中添加元素,然后创建线程,在启动线程之前,获取到了arrayList迭代器;先线程启动之后,对arrayList进行修改其第二个元素的值,然后删除了arrayList的两个元素。主线程在子线程执行完毕后,遍历打印arrayList元素,从打印的结果可以看出,在子线程中对arrayList的修改与删除操作并没有生效,这就是迭代器弱一致性的体现。注意:获取迭代器的操作必须在子线程启动执行之前进行。
另外补充一点,CopyOnWriteArrayList使用写时复制的策略来保证list的一致性,而获取——修改——写入三步操作并不是原子的,所以在增删改的操作过程中都使用了独占锁,来保证在某个时间只有一个线程能对list数组进行修改。CopyOnWriteArrayList提供了弱一致性的迭代器,从而保证在获取迭代器后,其他线程对list的修改是不可见的,迭代器遍历的数组是一个快照。