Java迭代器背后的概念是什么?

4

我正在研究Java的iterator接口,但不明白为什么它被设计成这样。

为什么Java Iterator要使用hasNextnext方法而不是将它们合并成一个方法?

以下是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可以是一个值,所以我的方法是无效的。但是否有可能绕过我上面提到的问题?


дҪ жҳҜеҗҰжӢ…еҝғдҪ зҡ„жәҗд»Јз Ғжё…жҷ°еәҰпјҢжҲ–иҖ…жӢ…еҝғи°ғз”ЁhasNext()е’Ңnext()дјҡеёҰжқҘжҖ§иғҪжҲҗжң¬пјҹ - Andrew Spencer
我正在进行一门与Java迭代器相关的大学课程项目,所以想更多地了解它。 - Bryan Chen
好的。在现实世界中,源代码的清晰度几乎总是比微小的性能调整更重要。你必须执行许多循环,才能使额外的方法调用成为执行时间的重要贡献者。 - Andrew Spencer
3个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
7

你的建议会使集合中的null值不可用,因为它使用null作为“毒药”,以检测迭代结束。

在极少数情况下,两个线程共享一个迭代器,您只需要将其包装在一些自定义类中,并同步访问迭代器,使检查然后执行操作变成原子操作。这是必需的,因为即使迭代器只有一个方法,在你的示例中支持集合(ArrayList)也不是线程安全的。


哦,我从来没有想过可以将 null 放入列表中... 太习惯以 Objective-C 的方式思考了。 - Bryan Chen
+1 但我会加上第二个简短的句子,说明原因。有两种状态,然后使 next() 返回 null:null 值或没有更多元素。要解决这个问题,您需要禁止使用 null 作为值。 - Fabian Barney
@FabianBarney 禁止使用 null 作为值可能并不是一个那么糟糕的想法,但要注意,我仍然不会将其用于表示 Java Iterator 的结束:这会违反契约。 - Andrew Spencer
1
@AndrewSpencer 我同意在项目中限制使用 null 可能是有道理的。然而,在像集合这样的可迭代对象中,null 确实是有意义的,而且我认为不应该被普遍禁止。想想存储在集合中的数据库结果等情况。 - Fabian Barney

4

你的第一个建议设计不好,因为它依赖于抛出和捕获异常来处理已知会最终发生的情况。异常是相当昂贵的,只应该用于通常不会发生的“异常”情况。

你的第二个建议没有考虑到 Iterable 可能有空元素。

至于线程安全的问题,是的,标准的 Iterator 倾向于不是线程安全的,需要一个带有额外开销的自定义实现。这对于大多数 Java 结构都是正确的。正如 JB Nizet 所指出的那样,更重要的是在其 Iterator 可以使用之前,Iterable 结构本身是否是线程安全的。


2
为了提高源代码的清晰度,可以使用(以字符串集合为例):
Iterable<String> values = ... // typically a Collection (List, Set...)

for (String value : values) {
    // do something with the value
}

我同意之前有关空值限定集合、异常控制循环(这是极其糟糕的形式)和线程安全的回复。

在你的建议中,对集合进行空值限定是最不明智的选择,特别是如果你的代码中有“无空值”策略。然而,这在 Java 中非常不习惯用(编辑:并且违反了 Iterator 接口的协定),所以可能会使未来的代码维护者混淆(编辑:并可能导致微妙和/或意外的错误)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,