我正在研究java iterator
接口,无法理解为什么它的设计是这样的。
为什么java迭代器使用hasNext
和next
将它们合并为一个方法?
这是java iterator的典型用法
Iterator iter = //iterator from a list
while(iter.hasNext()){
Object obj = iter.next();
// do something to obj
}
为什么不
Iterator iter = //iterator from a list
Object obj = null;
try {
while(true){
obj = iter.next();
// do something to obj
}
} catch (NoSuchElementException e) {}
很明显,这种方法看起来很丑陋但是如果next
在到达结束时返回null而不是抛出异常会发生什么?比代码可以简化为
Iterator iter = //iterator from a list
Object obj = null;
while((obj = iter.next()) != null){
// do something to obj
}
这就是Objective-C中的NSEnumerator如何工作
NSEnumerator *enumerator = // from an array
while (id obj = [enumerator nextObject]) {
// do something to obj
}
这增加了实现自定义iterator
的开销。
这也使java迭代器不是线程安全的。例如,ArrayList中包含一个元素。两个线程同时要求该列表hasNext
使用相同的迭代器。两个线程都将看到true
,并且它们将在该迭代器上调用next
。因为只有一个元素,并且迭代器被要求两次,这肯定会导致异常或错误状态。
我知道有一些线程安全的迭代器,但我不确定它的实现方式,但我认为会发生大量阻塞,导致效率低下。
我认为问题是检查和更新不是原子地发生的,我无法理解为什么java设计iterator
这样的接口。
更新
我看到null可以是一个值,所以我的方法无效。但是我可以解决上面提到的问题吗?
答案 0 :(得分:7)
您的命题将无法在集合中包含null
值,因为它使用null
作为“毒丸”来检测迭代的结束。
在非常罕见的情况下,两个线程共享一个迭代器,你只需要将它包装在一些自定义类中并同步对迭代器的访问,以使check-then-act操作成为原子。无论如何这都需要,因为即使迭代器只有一个方法,后台集合(在您的示例中为ArrayList)也不是线程安全的。
答案 1 :(得分:4)
你的第一个建议是糟糕的设计,因为它依赖于抛出并捕获一个已知最终会发生的条件的异常。例外情况相当昂贵,仅适用于通常不会出现的“例外”情况。
您的第二个建议未考虑Iterable
s可以包含空元素。
对于线程安全位,是标准Iterator
s往往不是线程安全的,并且需要增加额外开销的自定义实现。大多数Java结构都是如此。作为JB Nizet notes,Iterable
结构在其Iterator
之前是线程安全的更重要。
答案 2 :(得分:2)
为了改进源代码清晰度,请使用(使用字符串集合的示例)
Iterable<String> values = ... // typically a Collection (List, Set...)
for (String value : values) {
// do something with the value
}
我同意之前关于空有界集合的回复,带有异常的循环控制(这是一种恶劣的形式)和线程安全。
在您的建议中,对集合进行空绑定是最不明智的,特别是如果您的代码中有“无空”策略。然而,这是非常独特的Java(编辑:和打破了Iterator接口的合同),因此容易混淆代码的未来维护者(编辑:并可能导致细微和/或意外的错误)。