我们知道 Java 具有快速故障和故障安全的迭代设计。
我想知道,为什么我们基本上需要这些。这种分离有什么根本原因吗?. 这种设计有什么好处?
除了集合迭代之外,Java 在其他任何地方都遵循这种设计吗?
谢谢
我们知道 Java 具有快速故障和故障安全的迭代设计。
我想知道,为什么我们基本上需要这些。这种分离有什么根本原因吗?. 这种设计有什么好处?
除了集合迭代之外,Java 在其他任何地方都遵循这种设计吗?
谢谢
对于程序员来说,如果代码包含错误,则尽可能快地尝试失败是很有用的。最好的是在编译时,但有时这是不可能的。
因此,可以更早地检测到错误。否则,可能会发生错误仍未被发现,并且在您的软件中包含多年且没有人注意到该错误。
所以目标是尽早发现错误和错误。因此,接受参数的方法应该立即检查它们是否在允许的范围内,这将是迈向快速失败的一步。
这些设计目标无处不在,不仅仅是在迭代中,仅仅因为它是一个好主意。但有时您想在可用性或其他因素之间进行权衡。
例如,Set
实现允许null
值是许多错误的来源。新的 Java 9 集合不允许这样做,并在尝试添加此类时立即抛出异常,这是fail-fast。
您的迭代器也是一个很好的例子。我们有一些快速失败的迭代器,它们不允许在迭代时修改集合。主要原因是因为它很容易成为错误的来源。因此他们抛出一个ConcurrentModificationException
.
在迭代的特殊情况下,有人需要指定事情应该如何工作。当一个新元素在迭代过程中出现或被删除时,迭代应该如何表现?应该在迭代中包含/删除它还是应该忽略它?
例如,ListIterator
提供了一个非常明确的选项来迭代List
和操作它。在那里,您只允许操作迭代器当前所在的对象,这使其成为定义良好的对象。
另一方面,有一些故障安全迭代方法,例如使用ConcurrentHashMap
. 它们不会抛出此类异常并允许修改。然而,他们在原始收藏的副本上工作,这也解决了“如何”的问题。因此地图上的更改不会反映在迭代器中,迭代器在迭代时完全忽略任何更改。这会增加成本,每次迭代都必须创建完整的副本。
就我个人而言,那些故障安全变体不是一种选择。我使用快速失败的变体并记住我想要操作的内容。迭代完成后,我执行那些记忆的操作。
这对程序员来说写起来不太舒服,但你可以获得fail-fast的优势。这就是我要权衡可用性的意思。