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

时间:2012-03-02 09:08:01

标签: java iterator concept

我正在研究java iterator接口,无法理解为什么它的设计是这样的。

为什么java迭代器使用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可以是一个值,所以我的方法无效。但是我可以解决上面提到的问题吗?

3 个答案:

答案 0 :(得分:7)

您的命题将无法在集合中包含null值,因为它使用null作为“毒丸”来检测迭代的结束。

在非常罕见的情况下,两个线程共享一个迭代器,你只需要将它包装在一些自定义类中并同步对迭代器的访问,以使check-then-act操作成为原子。无论如何这都需要,因为即使迭代器只有一个方法,后台集合(在您的示例中为ArrayList)也不是线程安全的。

答案 1 :(得分:4)

你的第一个建议是糟糕的设计,因为它依赖于抛出并捕获一个已知最终会发生的条件的异常。例外情况相当昂贵,仅适用于通常不会出现的“例外”情况。

您的第二个建议未考虑Iterable s可以包含空元素。

对于线程安全位,是标准Iterator s往往不是线程安全的,并且需要增加额外开销的自定义实现。大多数Java结构都是如此。作为JB Nizet notesIterable结构在其Iterator之前是线程安全的更重要。

答案 2 :(得分:2)

为了改进源代码清晰度,请使用(使用字符串集合的示例)

Iterable<String> values = ... // typically a Collection (List, Set...)

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

我同意之前关于空有界集合的回复,带有异常的循环控制(这是一种恶劣的形式)和线程安全。

在您的建议中,对集合进行空绑定是最不明智的,特别是如果您的代码中有“无空”策略。然而,这是非常独特的Java(编辑:打破了Iterator接口的合同),因此容易混淆代码的未来维护者(编辑:并可能导致细微和/或意外的错误)。