【问题标题】:How to use the NoSuchElementException in Iterator.next()?如何在 Iterator.next() 中使用 NoSuchElementException?
【发布时间】:2023-03-21 10:17:01
【问题描述】:

我想知道为什么 Iterator.next() 会抛出 NoSuchElementException。通常,如果您使用迭代器,您会执行以下操作:

for(MyClass o: myFancyObject) {
    o.doSomething();
}

将被编译为

Iterator it = myFancyObject.iterator();
while(it.hasNext()) {
    MyClass o = (MyClass)(iterator.next());
    o.doSomething();
}

但是为什么 next() 方法会抛出 NoSuchElementException 呢?

所以让我们在 next() 方法中调用 hasNext() 方法,如下所示:

public MyClass next() {
    if (! hasNext())
        throw new NoSuchElementException();
    return mySecretDataModel.getElement(counter++);
}

但随后我检测到以下问题: hasNext() 方法将在每次迭代中被调用两次。首先它是从使用迭代器的方法调用的,然后是第二次由 next() 方法调用。这会花费不必要的计算时间。这是 Iterator 接口的作者的意图还是仅仅是设计疏忽?

【问题讨论】:

  • 这个问题确实有点不清楚。 “但后来我检测到以下问题:hasNext() 方法将在每次迭代中调用两次。首先它从使用迭代器的方法调用,然后第二次由 next() 方法调用”这通常被认为是不好的练习,我想。这被称为副作用。由于您注意到的问题,这很糟糕。
  • Iterator 的 JavaDoc 说:“抛出:NoSuchElementException - 如果迭代没有更多元素” 那么,我是否需要遵守这个合同,或者仅仅实现 hasNext() 就足够了?
  • 我觉得没问题,只要方法是idempotent
  • @MariusSpix: hasNext() 可能非常快,打破 Iterator 的合同以每小时获得几纳秒是一个坏主意。不要预先优化:它是万恶之源。并相信 JIT 可以将您经常使用的代码编译为快速的原生代码。

标签: java design-patterns interface iterator


【解决方案1】:

iterator 可能会抛出 NoSuchElementException 有几个原因,但最明显的是调用者没有调用 hasNext() 或忽略结果。

【讨论】:

    【解决方案2】:

    你会这样写

    public MyClass next() {
        if (counter >= mySecretDataModel.getSize())
            throw new NoSuchElementException();
        return mySecretDataModel.getElement(counter++);
    }
    

    【讨论】:

    • 但是“if (counter >= mySecretDataModel.size())”也被签入了hasNext()……所以还是有双重检查。
    • @MariusSpix 在这种情况下,它不会检查不能执行两次的任何事情,并且性能成本是名义上的,因为分支预测将跳过条件为假的常见情况。
    【解决方案3】:

    我认为不需要致电hasNext(),更好的方法是:

    public MyClass next() {
        if (counter >= mySecretDataModel.size())
            throw new NoSuchElementException();
    
        return mySecretDataModel.getElement(counter++);
    }
    

    另一种选择是将迭代器状态存储在hasNext() 期间计算的字段中,即一对MyObject nextElementboolean hasNext,因此没有两次调用。

    【讨论】:

      【解决方案4】:

      你在这两个陈述中都绝对正确。第一个(尽管这里有所有其他答案)是我们应该从next() 调用hasNext(),以避免代码重复。第二个是调用hasNext() 两次是不必要的操作,计算成本明智。

      由于是defensive,我们正在从next() 拨打hasNext()。我们可以跳过这个操作,让next() 假设hasNext() 已经被调用并且我们的客户知道他在做什么。但这可能会导致意外和不可预测的异常,例如NullPointerException

      所以,这里的答案是 - defensive programming 是个好东西,尽管它需要一些成本。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-19
        • 1970-01-01
        相关资源
        最近更新 更多