是一个"无限"迭代器糟糕的设计?

Ada*_*ski 66 java collections iterator

通常认为提供Iterator"无限"的实现是不好的做法; 即对hasNext()always(*)的调用返回true的位置?

通常我会说"是",因为调用代码可能表现不正常,但在下面的实现hasNext()中将返回true,除非调用者从List中删除迭代器初始化的所有元素; 即存在终止条件.你认为这是合法用途Iterator吗?它似乎没有违反合同,虽然我认为有人可能认为这是不直观的.

public class CyclicIterator<T> implements Iterator<T> {
  private final List<T> l;
  private Iterator<T> it;

  public CyclicIterator<T>(List<T> l) {
    this.l = l;
    this.it = l.iterator();
  }

  public boolean hasNext() {
    return !l.isEmpty();
  }

  public T next() {
    T ret;

    if (!hasNext()) {
      throw new NoSuchElementException();
    } else if (it.hasNext()) {
      ret = it.next();
    } else {
      it = l.iterator();
      ret = it.next();
    }

    return ret;
  }

  public void remove() {
    it.remove();
  }
}
Run Code Online (Sandbox Code Playgroud)

(迂腐)编辑

有些人评论了如何Iterator使用一个无限序列(如Fibonacci序列)生成值.但是,Java Iterator文档声明Iterator是:

集合上的迭代器.

现在你可以争辩说Fibonacci序列是一个无限集合但是在Java中我会将集合与java.util.Collection接口等同起来,它提供了size()暗示集合必须有界的方法.因此,Iterator从无界序列中使用作为值的生成器是否合法?

oxb*_*kes 76

我认为这完全合法 - Iterator只是一堆"东西".为什么流必然是有界的?

许多其他语言(例如Scala)具有内置于其中的无界流的概念,这些可以迭代.例如,使用scalaz

scala> val fibs = (0, 1).iterate[Stream](t2 => t2._2 -> (t2._1 + t2._2)).map(_._1).iterator
fibs: Iterator[Int] = non-empty iterator

scala> fibs.take(10).mkString(", ") //first 10 fibonnacci numbers
res0: String = 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
Run Code Online (Sandbox Code Playgroud)

编辑: 就最小惊喜的原则而言,我认为这完全取决于背景.例如,我期望这种方法返回什么?

public Iterator<Integer> fibonacciSequence();
Run Code Online (Sandbox Code Playgroud)

  • +1非常好的一点.它改变了我的想法. (4认同)
  • 原则不是教条.随意做一个例外但是让它变得明显,这是一个例外.比如,在函数描述的顶部以粗体记录此行为,并使名称自我解释. (3认同)

Jör*_*tag 19

整点Iterator是,它是懒惰的,即它可以让你只为你问尽可能多的对象.如果用户要求无限的所有对象Iterator,那就是他们的问题,而不是你的问题.


Joa*_*uer 7

虽然我也认为这是合法的,但我想补充说,这样一个Iterator(或更确切地说:Iterable产生这样的一个Iterator)不会很好地与增强的for循环(也就是每个循环):

for (Object o : myIterable) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

由于增强型for循环中的代码无法直接访问迭代器,因此无法调用remove()迭代器.

因此,要结束循环,必须执行以下操作之一:

  • 获取访问内部ListIterator和直接删除的对象,可能挑起ConcurrentModificationException
  • 使用break退出循环
  • "使用"a Exception退出循环
  • 使用return离开环路

除了最后一个替代方案之外的所有方案都不是保留增强的for循环的最佳方法.

当然,"正常"for循环(或任何其他循环与显式Iterator变量一起)可以正常工作:

for (Iterator it = getIterator(); it.hasNext();) {
  Object o = it.next()
  ...
}
Run Code Online (Sandbox Code Playgroud)

  • @Konrad:它们是可以接受的,但增强的for循环**强**暗示我想迭代'Iterator`提供的*every*元素.类似地,几乎没有人会期望在这样开始的循环中出现`break`或`return`:`for(int i = 0; i <10; i ++)`. (6认同)
  • ...或者使用"return"退出循环,如果循环的意图是找到特定元素,这实际上可能是一种很好的方法. (4认同)
  • 我不同意.至少`break`(和`return`)是完全可以接受的循环方式. (4认同)

Mar*_*ams 7

这可能是语义,但迭代器应该努力返回下一个项目,而不考虑它何时结束.结束是一个集合的副作用,虽然它可能看起来像一个常见的副作用.

那么,无限和10万亿件物品的收藏之间有什么区别?呼叫者要么全部都想要,要么他不想要.让来电者决定要返回多少项或何时结束.

我不会说调用者不能使用for-each构造.他可以,只要他想要所有物品.

集合的文档中的某些内容(如"可能是无限集合")是合适的,但不是"无限迭代器".

  • 是! 想想一个网络服务器:它只是针对无限的HTTP请求流的每个循环.到底为什么要终止?事实上,如果一个网络服务器*确实*终止,我们通常称之为"崩溃",并对此感到不安.你*希望*网络服务器是一个无限循环! (2认同)