Fail-Safe Iterator vs Fail-Fast Iterator – 失败安全迭代器与失败快速迭代器对比

最后修改: 2017年 12月 26日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this article, we’ll introduce the concept of Fail-Fast and Fail-Safe Iterators.

在这篇文章中,我们将介绍Fail-Fast和Fail-SafeIterators的概念。

Fail-Fast systems abort operation as-fast-as-possible exposing failures immediately and stopping the whole operation.

故障快速系统尽可能快地中止操作,立即暴露故障并停止整个操作。

Whereas, Fail-Safe systems don’t abort an operation in the case of a failure. Such systems try to avoid raising failures as much as possible.

故障安全系统在发生故障的情况下不会中止一个操作。这类系统尽可能地避免引发故障。

2. Fail-Fast Iterators

2.快速失败的迭代器

Fail-fast iterators in Java don’t play along when the underlying collection gets modified.

当底层集合被修改时,Java中的故障快速迭代器并不配合。

Collections maintain an internal counter called modCount. Each time an item is added or removed from the Collection, this counter gets incremented.

Collections维护一个名为modCount的内部计数器。每当一个项目被添加或从Collection中移除,这个计数器就会被递增。

When iterating, on each next() call, the current value of modCount gets compared with the initial value. If there’s a mismatch, it throws ConcurrentModificationException which aborts the entire operation.

当迭代时,在每次next()调用时,modCount的当前值会与初始值进行比较。如果有一个不匹配,它会抛出ConcurrentModificationException,从而中止整个操作。

Default iterators for Collections from java.util package such as ArrayList, HashMap, etc. are Fail-Fast.

来自java.util包集合的默认迭代器,如ArrayListHashMap等,是Fail-Fast。

ArrayList<Integer> numbers = // ...

Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    Integer number = iterator.next();
    numbers.add(50);
}

In the code snippet above, the ConcurrentModificationException gets thrown at the beginning of a next iteration cycle after the modification was performed.

在上面的代码片段中,ConcurrentModificationException在执行修改后的下一个迭代周期开始时被抛出。

The Fail-Fast behavior isn’t guaranteed to happen in all scenarios as it’s impossible to predict behavior in case of concurrent modifications. These iterators throw ConcurrentModificationException on a best effort basis.

Fail-Fast行为并不保证在所有情况下都会发生,因为不可能预测并发修改时的行为。这些迭代器会尽力抛出ConcurrentModificationException

If during iteration over a Collection, an item is removed using Iterator‘s remove() method, that’s entirely safe and doesn’t throw an exception.

如果在Collection的迭代过程中,使用Iteratorremove()方法删除一个项目,这完全是安全的,不会抛出一个异常

However, if the Collection‘s remove() method is used for removing an element, it throws an exception:

然而,如果Collectionremove()方法被用来删除一个元素,它会抛出一个异常。

ArrayList<Integer> numbers = // ...

Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
    if (iterator.next() == 30) {
        iterator.remove(); // ok!
    }
}

iterator = numbers.iterator();
while (iterator.hasNext()) {
    if (iterator.next() == 40) {
        numbers.remove(2); // exception
    }
}

3. Fail-Safe Iterators

3.失败安全的迭代器

Fail-Safe iterators favor lack of failures over the inconvenience of exception handling.

失败安全的迭代器更倾向于没有失败,而不是异常处理的不便。

Those iterators create a clone of the actual Collection and iterate over it. If any modification happens after the iterator is created, the copy still remains untouched. Hence, these Iterators continue looping over the Collection even if it’s modified.

这些迭代器创建了一个实际Collection的克隆,并对其进行迭代。如果在迭代器创建后发生了任何修改,该副本仍然保持不动。因此,这些迭代器继续在Collection上循环,即使它被修改。

However, it’s important to remember that there’s no such thing as a truly Fail-Safe iterator. The correct term is Weakly Consistent.

然而,重要的是要记住,没有真正的Fail-Safe迭代器这回事。正确的说法是弱一致性。

That means, if a Collection is modified while being iterated over, what the Iterator sees is weakly guaranteed. This behavior may be different for different Collections and is documented in Javadocs of each such Collection.

这意味着,如果 Collection在被迭代时被修改,Iterator看到的是弱保证。这种行为对于不同的Collection可能是不同的,并且在每个这样的Collection的Javadocs中都有记录。

The Fail-Safe Iterators have a few disadvantages, though. One disadvantage is that the Iterator isn’t guaranteed to return updated data from the Collection, as it’s working on the clone instead of the actual Collection.

不过,Fail-Safe Iterators有一些缺点。一个缺点是,Iterator不能保证从Collection返回更新的数据,因为它是在克隆上工作,而不是在实际的Collection上工作。

Another disadvantage is the overhead of creating a copy of the Collection, both regarding time and memory.

另一个缺点是创建Collection副本的开销,包括时间和内存方面。

Iterators on Collections from java.util.concurrent package such as ConcurrentHashMap, CopyOnWriteArrayList, etc. are Fail-Safe in nature.

来自java.util.concurrent包的Collections上的迭代器,如ConcurrentHashMapCopyOnWriteArrayList等,在本质上是失败安全的。

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

map.put("First", 10);
map.put("Second", 20);
map.put("Third", 30);
map.put("Fourth", 40);

Iterator<String> iterator = map.keySet().iterator();

while (iterator.hasNext()) {
    String key = iterator.next();
    map.put("Fifth", 50);
}

In the code snippet above, we’re using Fail-Safe Iterator. Hence, even though a new element is added to the Collection during the iteration, it doesn’t throw an exception.

在上面的代码片段中,我们使用的是Fail-Safe Iterator。因此,即使在迭代过程中,一个新的元素被添加到Collection中,它也不会抛出一个异常。

The default iterator for the ConcurrentHashMap is weakly consistent. This means that this Iterator can tolerate concurrent modification, traverses elements as they existed when Iterator was constructed and may (but isn’t guaranteed to) reflect modifications to the Collection after the construction of the Iterator.

ConcurrentHashMap的默认迭代器是弱一致性的。这意味着这个迭代器可以容忍并发修改,遍历迭代器构建时存在的元素,并且可能(但不保证)反映迭代器构建后对集合的修改。

Hence, in the code snippet above, the iteration loops five times, which means it does detect the newly added element to the Collection.

因此,在上面的代码片段中,迭代循环了五次,这意味着它确实检测到了新添加到Collection的元素。

4. Conclusion

4.结论

In this tutorial, we’ve seen what Fail-Safe and Fail-Fast Iterators mean and how these are implemented in Java.

在本教程中,我们已经看到了Fail-Safe和Fail-Fast Iterators的含义以及如何在Java中实现这些。

The complete code presented in this article is available over on GitHub.

本文介绍的完整代码可在GitHub上获得over