Java Iterator实现 - next()和hasNext()强制执行命令

时间:2010-02-01 11:13:02

标签: java concurrency iterator

我有一个java.util.Iterator的实现,要求始终通过调用next()来调用hasNext()。 (这是因为结果是在多线程环境中异步返回的,并且永远不清楚可能会有多少结果)。

在JavaDoc中正确记录此文件是否“正确”,如果违反此规则,则抛出RuntimeException。或者这会使Iterator界面拉得太远吗?

所有的想法都赞赏?

8 个答案:

答案 0 :(得分:18)

我可能在这里遗漏了一些内容,但为什么不在内部调用hasNext()

答案 1 :(得分:15)

要求在hasNext()之前调用next()违反iterator合同。你真的应该重写它,以便next()如果没有要返回的元素,只会抛出NoSuchElementException

答案 2 :(得分:7)

我想你正在做这样的事情:

class IteratorImpl<T> implements Iterator<T> {
  private Source<T> source = ...
  private T next = null;

  public boolean hasNext() {
    if(next == null) {
      next = source.poll();
    }
    return next != null;
  }

这对我来说听起来不错。我无法想象你想要在没有next的情况下使用hasNext的情况 - 这将是异常的处方。


编辑:

hasNext()的{​​{3}}说:

  

如果迭代有更多元素,则返回true。 (换句话说,如果next会返回一个元素而不是抛出异常,则返回true。)

对我来说,实施不违反合同。但是,我会(doc)仍然将next()实现为:

  public T next() {
    if(!hasNext()) {
      throw new NoSuchElementException();
    }
    T ret = next;
    next = null;
    return ret;
  }

我的意思是,这项检查真的花了你多少钱?

您必须根据Fabian Steeg implies检查并抛出NoSuchElementException。我相信,对!hasNext()next == null的测试都符合此标准,但我赞成前者。

如果有人抓住NoSuchElementException而不是致电hasNext(),那么您可能会遇到更大的问题。

答案 3 :(得分:4)

如果您的hasNext()next()来电不在同步广告/方法中,即使您在{{1}之前拨打hasNext(),也无法保证您拥有元素}。

next()接口的约定是如果没有更多元素,则应该抛出Iterator。所以继续使用NoSuchElementException方法,直到出现这种异常。

那说,看一下java.util.concurrent包 - 它有并发集合,其迭代器可以帮助你 - 也就是说你可以使用这些集合和迭代器而不是实现自己的集合。

答案 4 :(得分:2)

当没有更多元素时,我宁愿从next()抛出异常。在多线程环境中,hasNext()无论如何都是无用的。

答案 5 :(得分:2)

您可以做的是自己实现Iterator界面(如果您需要与其他API连接)并要求您的客户实施您自己更严格的界面。

我在函数式编程库中创建了这样一个类,以便在工作中的项目中轻松实现Iterator {/ 1}}。

我的类EasierIterator实现了Iterator接口,同时要求客户端基于ResultSet / moveNext()实现更简单的接口。它实际上为你做了当前项目的缓存。

答案 6 :(得分:1)

您可以执行类似下面的操作,您可以将基础数据提取委托给私有方法,并实现hasNext()next()以对不存在数据做出不同反应。这样做的好处是,您可以在不先调用next()的情况下重复调用hasNext(),因此不会违反迭代器的合同

public class IteratorImpl<T> implements Iterator<T> {
  private final Source<T> source;
  private T next;

  public synchronized boolean hasNext() {
    tryGetNext();
    return next != null;
  }

  public synchronized T next() {
    tryGetNext();

    if (next == null) {
      throw new NoSuchElementException();
    } 

    return next;
  }

  private void tryGetNext() {
    if (next != null) {
      next = source.poll();
    }
  }
}

答案 7 :(得分:0)

编辑:在这个答案中,我试图争辩说这是问题所允许的。但我忽略了Iterator.hasNext文档中的一个句子,它使我的整个推理无效:

  

换句话说,如果next()返回一个元素而不是抛出异常,则返回true。

这似乎意味着反复调用next直到hasNext返回true并且调用next,直到得到NoSuchElementException应该返回相同的元素序列。

因此,问题似乎是不允许

原始答案

这是对规范律师类型答案的尝试。为清楚起见,我将以紧凑的形式重述问题:

  

Iterable规范是否允许在NoSuchElementException调用Iterator.next时调用Iterator.hasNext而不事先调用Iterator.hasNext,即使元素已返回NoSuchElementException先被叫了?

讨论

Iterator.hasNext州的文档:

  

如果迭代有更多元素,则返回true。

对于Iterator.next

  

抛出:NoSuchElementException - 如果迭代没有更多元素

显然,当#34;迭代没有更多元素&#34;时,它被允许抛出hasNext,但在此之前不会。这应该与Iterator返回false时一致。

这导致了一个问题:文档对于#34;迭代&#34;具体意味着什么?和&#34;元素&#34;? hasNext文档没有给出答案,这为实施者提供了一些摆动空间。

在我看来,有两种可能的解释:

  1. 从迭代器接口本身的角度来看,唯一存在的概念是&#34;迭代&#34;是&#34;只要next返回true&#34;。这意味着如果客户在hasNext之前调用T,他们不知道是否有更多元素,那么它是未定义的。

    因此,迭代器实现者的规范允许决定迭代已经完成。所以这个问题的答案是肯定的。

  2. 但是Iterable.iterator上的文档也提到了&#34;元素&#34;:

      

    返回类型为Iterable的元素的迭代器。

    那么&#34;元素&#34;这意味着什么它是否意味着&#34;实现next的集合中的所有元素?不,它并没有这么说,并不是所有的迭代都是拥有一组固定的元素。

    什么&#34;元素&#34;对于某个特定的可迭代的意思是由实施者决定。 &#34;元素的有效定义&#34;对于迭代器,可以是&#34; 集合中的所有元素,或者,客户决定在hasNext &#34;之前调用{{1}}之前的所有元素。

    因此,这个案例也得出结论,问题的答案是肯定的。 (但请看结尾处的注释!)

  3. 结论

    文档不是很清楚,但似乎问题的答案是:是的,这是允许的。

    注意

    如果迭代器完成了问题,那么当然应记录该行为。但是其他的迭代也应该记录它们的迭代器是哪些元素。

    例如,ArrayList.iterator文档清楚地说明了迭代器所在的元素:

      

    以适当的顺序返回此列表中元素的迭代器。

    最后的注意事项:是的,我花了很多时间在这上面我很疯狂。