Iterator
JAVA 集合主要分为两类:Collection 和 Map. 而 Collection 又继承了 Iterable 接口,Iterable 接口内只有一个 iterator 方法,返回一个 Iterator 迭代器
public interface Iterable<T> {
/**
* Returns an {@link Iterator} for the elements in this object.
*
* @return An {@code Iterator} instance.
*/
Iterator<T> iterator();
}
关于 Enumeration
- hasMoreElements() //是否还有元素
- nextElement() //返回下一个元素
Enumeration 接口早在 JDK 1.0 时就推出了,当时比较早的容器比如 Hashtable, Vector 都使用它作为遍历工具;
在 JDK 1.2 后,Iterator 替代了它的功能;
Iterator adds an optional remove operation, and has shorter method names
ConcurrentModificationException
- 当迭代器在调用 next、remove 方法时,会比较 expectedModCount 和 modCount 是否相等
- modCount 在每次 add、clear、remove 时都会修改
- 如果不相等,则抛出 ConcurrentModificationException,也就是 Fail-Fast 机制
modCount 和 expectedModCount
- modCount,记录了集合结构性变化的次数
- expectedModCount 的初值为 modCount
- hasNext 的判断条件为 cursor != size,就是当前迭代的位置不是数组的最大容量值就返回 true
- next 和 remove 操作之前都会先调用 checkForComodification 来检查 expectedModCount 和 modCount 是否相等
Fail-Fast 机制
java.util.HashMap、java.util.ArrayList 不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了 map 或 list,那么将抛出 ConcurrentModificationException,这就是所谓 fail-fast 策略。这一策略在源码中的实现是通过 modCount 域,modCount 顾名思义就是修改次数,对 map 或 list 内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等。
解决方案
- 方法一:用 CopyOnWriteArrayList,ConcurrentHashMap 替换 ArrayList, HashMap,它们的功能和名字一样,在写入时会创建一个 copy,然后在这个 copy 版本上进行修改操作,这样就不会影响原来的迭代。不过坏处就是浪费内存。
- 方法二:使用 Collections.synchronizedList 加 同步锁。
Iterator 使用方法
所有 Collection 的子类都有 iterator() 方法来获得 Iterator,通过 Iterator 的标准操作方法,可以让我们不必关心具体集合的类型,从而避免向客户端暴露出集合的内部结构。
- 不使用 Iterator 遍历集合是这样的:
for (int i = 0; i < size; i++) {
// ...
}
- 使用 Iterator 遍历集合是这样的:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
对比而言,后者客户端代码与具体集合类型耦合性弱,复用性更强。缺点就是无法获取指定的元素,只能挨个遍历。