为什么Java的迭代器不是Iterable?

Łuk*_*nik 172 java iterator iterable

为什么Iterator界面没有扩展Iterable

iterator()方法可以简单地返回this.

它是故意还是只是对Java设计师的监督?

能够使用像这样的迭代器的for-each循环会很方便:

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

where listSomeObjects()返回一个迭代器.

Jon*_*eet 212

迭代器是有状态的.这个想法是,如果你调用Iterable.iterator()两次,你将得到独立的迭代器 - 无论如何,对于大多数的迭代.在你的场景中显然不是这种情况.

例如,我通常可以写:

public void iterateOver(Iterable<String> strings)
{
    for (String x : strings)
    {
         System.out.println(x);
    }
    for (String x : strings)
    {
         System.out.println(x);
    }
}
Run Code Online (Sandbox Code Playgroud)

这应该打印两次集合 - 但是使用你的方案,第二个循环总是会立即终止.

  • @Chris:如果一个实现返回两次相同的迭代器,它究竟怎么能完成Iterator的合同呢?如果你调用`iterator`并使用结果,它必须迭代集合 - 如果同一个对象已经迭代了集合,它就不会这样做.你可以给*任何*正确的实现(除了空集合),其中两次返回相同的迭代器? (16认同)
  • 这是一个很好的回应乔恩,你真的得到了问题的症结所在.惭愧这不是公认的答案!Iterable的合同是严格定义的,但上面是一个很好的解释,为什么允许Iterator实现Iterable(对于foreach)会破坏接口的精神. (7认同)
  • @JonSkeet虽然Iterator <T>是有状态的,但是Iterable <T>的契约对于能够被使用两次来获得独立的迭代器没有任何说明,即使是在99%的情况下也是如此.所有Iterable <T>都说它允许一个对象成为foreach的目标.对于那些对Iterator <T>不是Iterable <T>不满意的人,你可以自由地制作这样一个Iterator <T>.它不会破坏合同一点.然而 - 迭代器本身不应该是可迭代的,因为这会使它循环依赖,并为icky设计奠定了基础. (3认同)
  • @Centril:对.编辑后指示*通常*调用`iterable`两次会给你独立的迭代器. (2认同)
  • 这确实触及了它的核心.几乎可以实现一个通过重置自身来实现.iterator()的Iterable Iterator,但是这种设计在某些情况下仍然会中断,例如,如果它被传递给一个接受Iterable并循环遍历所有可能的对的方法通过嵌套for-each循环的元素. (2认同)

Pau*_*ams 65

因为迭代器通常指向集合中的单个实例.Iterable意味着可以从对象获取迭代器来遍历其元素 - 并且不需要迭代单个实例,这是迭代器所代表的.

  • 虽然我同意答案,但我不知道我是否同意这种心态.Iterable接口提供了一个方法:Iterator <?> iterator(); 无论如何,我应该能够为for-each指定一个迭代器.我不买. (50认同)
  • +1:集合是可迭代的.迭代器不可迭代,因为它不是集合. (25认同)
  • @ S.Lott:收集与此讨论完全无关.集合只是Iterable的许多可能实现之一.事物不是集合的事实与它是否是Iterable无关. (24认同)
  • @ S.Lott那里有很好的循环推理. (23认同)
  • @ S.Lott最后一次尝试:集合∈可迭代.迭代器≠收藏∴迭代器∉可迭代 (14认同)
  • @ S.Lott你说:收集是可迭代的; 迭代器不可迭代,因为它不是集合.如果你不知道,那就是我们所说的循环推理.这就像是说:我太棒了.你不是我,所以你不是很棒. (6认同)
  • @Zefi:我还没看到圆形.仅仅重复这些话并没有帮助."迭代器"有一个定义; 它本身不可迭代,但适用于可迭代对象."Iterable"有一个定义.它不是迭代器,但与迭代器一起使用.由于"迭代器"和"可迭代"不是同一个东西 - 它们没有使用"is-a"定义 - 我看不到循环性.您将不得不提供更多细节来解释两个不同但相关的概念是如何"循环"的. (3认同)
  • @Zefi:那个通告怎么样? (2认同)
  • 答案是试图合理化坏的,反人类的设计.直观且有用的可重复意味着你可以迭代的东西.没有必要争论收集或任何其他狗屎.否则,如果您愿意,迭代器也是一个集合,您可以迭代它. (2认同)

Bar*_*ney 59

对于我的0.02美元,我完全同意Iterator不应该实现Iterable,但我认为增强的for循环应该接受.我认为整个"使迭代器可迭代"的论点出现在解决语言缺陷的过程中.

引入增强的for循环的全部原因是它"在迭代集合和数组时消除了迭代器和索引变量的苦差事和错误倾向"[ 1 ].

Collection<Item> items...

for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
    Item item = iter.next();
    ...
}

for (Item item : items) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

那为什么同样的论点不适用于迭代器呢?

Iterator<Iter> iter...
..
while (iter.hasNext()) {
    Item item = iter.next();
    ...
}

for (Item item : iter) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,都删除了对hasNext()和next()的调用,并且没有对内部循环中的迭代器的引用.是的,我理解Iterables可以被重用来创建多个迭代器,但是这一切都发生在for循环之外:在循环内部,迭代器返回的项目一次只有一个前进项目.

此外,允许这样也可以很容易地使用for循环for Enumerations,正如其他地方所指出的那样,它类似于Iterators而不是Iterables.

所以不要让Iterator实现Iterable,而是更新for循环以接受.

干杯,

  • 我同意.从理论上讲,在获取迭代器时,使用其中的一部分,然后将其放入foreach(打破foreach的"每个"合约)可能会产生混淆,但我认为这不足以成为不具备此功能的理由. (6认同)
  • 你是否使用`for(String x:strings){...}`或`while(strings.hasNext()){...}`样式并不重要:如果你试图遍历迭代器第二次两次将不会产生任何结果,所以我不认为这本身就是反对允许增强语法的论据.Jon的回答是不同的,因为他在展示如何在`Iterable'中包装`Iterator`会导致问题,因为在这种情况下,你可以期望能够多次重复使用它. (2认同)

McD*_*ell 17

正如其他人所指出的,Iterator并且Iterable是两个不同的东西.

此外,Iterator实现早于增强循环.

使用静态方法导入时使用简单的适配器方法来克服此限制也是微不足道的:

for (String line : in(lines)) {
  System.out.println(line);
}
Run Code Online (Sandbox Code Playgroud)

示例实施:

  /**
   * Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
   * loops. If {@link Iterable#iterator()} is invoked more than once, an
   * {@link IllegalStateException} is thrown.
   */
  public static <T> Iterable<T> in(final Iterator<T> iterator) {
    assert iterator != null;
    class SingleUseIterable implements Iterable<T> {
      private boolean used = false;

      @Override
      public Iterator<T> iterator() {
        if (used) {
          throw new IllegalStateException("SingleUseIterable already invoked");
        }
        used = true;
        return iterator;
      }
    }
    return new SingleUseIterable();
  }
Run Code Online (Sandbox Code Playgroud)

在Java 8适应的Iterator一种Iterable变简单了:

for (String s : (Iterable<String>) () -> iterator) {
Run Code Online (Sandbox Code Playgroud)

  • 感谢`for(String s:(Iterable &lt;String&gt;)()-&gt;迭代器)` (2认同)

and*_*oke 8

正如其他人所说,可以多次调用Iterable,每次调用返回一个新的Iterator; 迭代器只使用一次.所以他们是相关的,但服务于不同的目的.然而,令人沮丧的是,"compact for"方法仅适用于迭代.

我将在下面描述的是一种充分利用两个世界的方法 - 即使基础数据序列是一次性的,也可以返回Iterable(用于更好的语法).

诀窍是返回实际触发工作的Iterable的匿名实现.因此,不是执行生成一次性序列然后返回Iterator的工作,而是返回一个Iterable,每次访问它都会重做工作.这可能看起来很浪费,但通常你只会调用Iterable一次,即使你多次调用它,它仍然有合理的语义(不像一个简单的包装器使得Iterator"看起来像是一个Iterable,这赢了"如果使用两次则失败).

例如,假设我有一个DAO,它提供了一系列来自数据库的对象,我希望通过迭代器提供对它的访问(例如,如果不需要,则避免在内存中创建所有对象).现在我可以返回一个迭代器,但这会使循环中返回的值变得难看.所以我把所有内容都包装在一个anon Iterable中:

class MetricDao {
    ...
    /**
     * @return All known metrics.
     */
    public final Iterable<Metric> loadAll() {
        return new Iterable<Metric>() {
            @Override
            public Iterator<Metric> iterator() {
                return sessionFactory.getCurrentSession()
                        .createQuery("from Metric as metric")
                        .iterate();
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

然后可以在这样的代码中使用它:

class DaoUser {
    private MetricDao dao;
    for (Metric existing : dao.loadAll()) {
        // do stuff here...
    }
}
Run Code Online (Sandbox Code Playgroud)

这让我可以使用紧凑的for循环,同时仍然保持增量内存使用.

这种方法是"懒惰的" - 当请求Iterable时,工作没有完成,但只有在内容被迭代后才能完成 - 你需要知道它的后果.在带有DAO的示例中,这意味着在数据库事务中迭代结果.

所以有各种各样的警告,但在许多情况下,这仍然是一个有用的习惯用语.


Luk*_*der 7

令人难以置信的是,还没有人给出这个答案.以下是Iterator使用新的Java 8 Iterator.forEachRemaining()方法"轻松"迭代的方法:

Iterator<String> it = ...
it.forEachRemaining(System.out::println);
Run Code Online (Sandbox Code Playgroud)

当然,有一个"更简单"的解决方案可以直接使用foreach循环,包含IteratorIterablelambda中:

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);
Run Code Online (Sandbox Code Playgroud)


Ste*_*e K 5

Iterator是一个允许您迭代某些东西的界面.它是通过某种集合进行移动的实现.

Iterable 是一个功能接口,表示某些东西包含一个可访问的迭代器.

在Java8中,这让生活变得非常简单......如果你有一个Iterator但需要一个,Iterable你可以简单地做:

Iterator<T> someIterator;
Iterable<T> = ()->someIterator;
Run Code Online (Sandbox Code Playgroud)

这也适用于for循环:

for (T item : ()->someIterator){
    //doSomething with item
}
Run Code Online (Sandbox Code Playgroud)