java的迭代器
iterable接口
内部通过iterator()方法返回一直iterator实例,iterator才是实际执行遍历操作的对象
所有实现iterator的类都可以使用增强for循环,底层实际上是获取iteraior进行遍历
iterator接口
boolean hasNext(); // 是否还有下一个元素
E next(); // 获取下一个元素
void remove(); // 移除当前元素(可选)
支持边遍历边修改
遍历是单向的
例如ArrayList和LinkedList的hasNext的底层原理都是维护一个游标cursor表示当前位置,来与列表大小进行比较
ListIterator接口
list的双向迭代器
public interface ListIterator<E> extends Iterator<E> {boolean hasPrevious();E previous();int nextIndex();int previousIndex();void set(E e); // 替换当前元素void add(E e); // 添加新元素
}
Spliterator接口
public interface Spliterator<T> {boolean tryAdvance(Consumer<? super T> action);Spliterator<T> trySplit(); // 拆分long estimateSize(); // 估计剩余数量int characteristics(); // 特性标志位
}List<String> list = Arrays.asList("A", "B", "C", "D", "E");
Spliterator<String> spliterator = list.spliterator();Spliterator<String> part1 = spliterator.trySplit();
if (part1 != null) {part1.forEachRemaining(System.out::println); // 处理前半部分
}
spliterator.forEachRemaining(System.out::println); // 处理后半部分
迭代器行为机制
fail-fast
当检测到在迭代过程中发生了结构性的改变(添加、删除元素),迭代器会立即抛出ConcurrentModificationException异常
这种机制依赖一个计数器来跟踪集合结构的变化,如果在迭代期间发现这个计数器与预期不符,就会认为这个集合被并发修改了,立即抛出异常
大多数标准的java集合类默认使用的都是fail-fast
例如在遍历arraylistist通过list.add()或者list.remove()修改列表,同时又使用迭代器遍历,就会抛出异常
这是因为arraylist内部使用一个modCount的字段记录集合被修改的次数,迭代器内部也维护了一个对modCount的引用,每次next()或hasNext()都会检查当前这个modCount和迭代器创建时的expectedModeCount是否相等,不想等就抛出异常【所以抛出异常的时机是迭代器执行next()或hasNext()的时候,不是修改集合的时候】
使用迭代器提供的remove()方法的话,不仅移除集合中元素,还会同步更新迭代器内的expectedModeCount,因此不会触发异常
fail-safe
即使集合在迭代过程中被修改了,也不会抛出异常,因为他们工作在一个集合的快照上,
迭代器访问的是集合的一个副本或视图,因此对原始集合的任何修改都不会影响正在进行中的迭代过程
但是看到的可能是过期的数据,因为反应的是迭代开始时刻的数据
CopyOnWriteArrayList、ConcurrentHashMap提供的就是fail-safe