【问题标题】:Java: why can't iterate over an iterator?Java:为什么不能迭代迭代器?
【发布时间】:2011-02-05 14:15:25
【问题描述】:

我看了Why is Java's Iterator not an Iterable?Why aren't Enumerations Iterable?,但我还是不明白为什么会这样:

void foo(Iterator<X> it) {
  for (X x : it) {
    bar(x);
    baz(x);
  }
}

无法实现。换句话说,除非我遗漏了什么,否则上述内容可能是很好且有效的语法糖:

void foo(Iterator<X> it) {
  for (X x; it.hasNext();) {
    x = it.next();
    bar(x);
    baz(x);
  }
}

【问题讨论】:

    标签: java iterator iterable


    【解决方案1】:

    其实可以的。

    在 java 8 上有很短的解决方法:

    for (X item : (Iterable<X>) () -> iterator)
    

    该技巧的详细解释见How to iterate with foreach loop over java 8 stream

    并且可以在相关问题中找到一些解释为什么它不被原生支持:

    Why does Stream<T> not implement Iterable<T>?

    【讨论】:

    • 谢谢,但并没有真正回答我的问题。问题(从 2010 年开始)实际上是关于设计选择(因此是“为什么”,而不是“如何”)。 +1 因为很高兴知道。
    【解决方案2】:

    这很可能是因为迭代器不可重用;每次要迭代元素时,都需要从 Iterable 集合中获取一个新的迭代器。但是,作为一种快速解决方法:

    private static <T> Iterable<T> iterable(final Iterator<T> it){
         return new Iterable<T>(){ public Iterator<T> iterator(){ return it; } };
    }
    
    //....
    {
         // ...
         // Now we can use:
         for ( X x : iterable(it) ){
            // do something with x
         }
         // ...
    }
    //....
    

    也就是说,最好的办法是简单地传递Iterable&lt;T&gt; 接口而不是Iterator&lt;T&gt;

    【讨论】:

    • +1: 使用数组和Iterable,您可以连续两次编写相同的循环并使其按预期工作。这不适用于Iterator
    • 你说得对,这将是最好的,但我使用我自己的迭代器而不是真正的集合(它是一个 DOM NodeList)。否则,您的回答是有道理的,谢谢。
    • @noamtm: 如果它不是Collection 但可以提供Iterator,那么它可能应该实现Iterable
    • Joachim:可能有不止一种方法可以迭代某些东西(例如Iterator&lt;T&gt; iterateInNaturalOrder()Iterator&lt;T&gt; iterateInSortedOrder()Iterator&lt;T&gt; iterateOverValidValues()),在这种情况下,Iterable合适,因为它只提供了一种迭代集合的方法,即iterator()
    • @JasonS an Iterable 在这些情况下仍然可以用作这些方法的返回类型(尽管显然单个 Iterable 是不够的)。
    【解决方案3】:

    for(Type t : iterable) 语法仅对实现 Iterable&lt;Type&gt; 的类有效。

    迭代器不实现可迭代。

    您可以迭代 Collection&lt;T&gt;List&lt;T&gt;Set&lt;T&gt; 之类的东西,因为它们实现了 Iterable。

    下面的代码是等价的:

    for (Type t: list) {
        // do something with t
    }
    

    Iterator<Type> iter = list.iterator();
    while (iter.hasNext()) {
        t = iter.next();
        // do something with t
    }
    

    之所以无法做到这一点,是因为在语言中添加了 for-each 语法以抽象出Iterator。使 for-each 循环与迭代器一起工作不会完成创建 for-each 循环的目的。

    【讨论】:

    • 这很明显,它并没有回答我“为什么不能实现它”的问题。
    • A Map(应该是 Map&lt;K,V&gt;,顺便说一句)实现 Iterable。您必须使用 keySet()values()entrySet() 并遍历它们。
    • @Joach 糟糕,我在考虑 C#。
    • 很有趣,所以IDictionary(我假设它是Map 的.NET 等效项)提供了IEnumerator。那列举了什么呢?它是否返回条目?价值观?钥匙?我似乎无法从文档中得到:msdn.microsoft.com/en-us/library/…
    • @joach 它遍历Entry&lt;Key, Value&gt;。因此,您每次迭代都会获得一个条目。它基本上是一个键值对。我真的很喜欢。
    【解决方案4】:

    迭代器并不意味着可以重复使用(即:在多个迭代循环中使用)。特别是,Iterator.hasNext() 保证您可以安全地调用 Iterator.next() 并确实从基础集合中获取下一个值。

    当在两个同时运行的迭代中使用同一个迭代器时(让我们假设一个多线程场景),这个承诺就不能再保持:

    while(iter.hasNext() {
       // Now a context switch happens, another thread is performing
       //    iter.hasNext(); x = iter.next();
    
      String s = iter.next();  
              // A runtime exception is thrown because the iterator was 
              // exhausted by the other thread
    }
    

    这样的场景完全破坏了 Iterator 提供的协议。实际上,它们甚至可以出现在单线程程序中:一个迭代循环调用另一个方法,该方法使用相同的迭代器来执行它自己的迭代。当此方法返回时,调用者正在发出一个 Iterator.next() 调用,但又失败了。

    【讨论】:

      【解决方案5】:

      但我仍然不明白为什么这个 [...] 无法实现。

      我可以看到几个原因:

      1. Iterators 不可重用,因此 for/each 会消耗迭代器 - 也许不是不正确的行为,但对于那些不知道如何减少 for/each 的人来说是不直观的。
      2. Iterators 不会经常在代码中“裸露”,因此它会使 JLS 变得复杂而收益甚微(for/each 结构已经够糟糕了,同时在 Iterables 和数组上工作)。
      3. 有一个easy workaround。仅仅为此分配一个新对象似乎有点浪费,但是分配很便宜,而且在大多数情况下,逃逸分析甚至可以免除您的小成本。 (不过,为什么他们没有在 Iterables 实用程序类中包含此解决方法,类似于 CollectionsArrays,但我无法理解。)
      4. (可能不是真的 - 请参阅 cmets。)我似乎记得 JLS 只能引用 java.lang[需要引用] 中的内容,因此他们必须创建java.lang 中的 Iterator 接口,java.util.Iterator 在没有添加任何内容的情况下扩展。现在我们有两个功能等效的迭代器接口。 50% 使用裸迭代器的新代码将选择java.lang 版本,其余使用java.util 中的版本。随之而来的混乱,兼容性问题比比皆是,等等。

      我认为第 1-3 点非常符合 Java 语言设计理念的走向:不要让新手感到惊讶,如果没有明显的收益超过成本,不要使规范复杂化, 不要用语言特性来做图书馆可以做的事情。

      同样的论点可以解释为什么java.util.Enumeration 也不是Iterable

      【讨论】:

      • “我似乎记得 JLS 只能引用 java.lang 中的东西” 忽略任何“讨论”或“示例”上下文(其中有很多),JLS 多次引用 java .io.Serializable(第 4.10.3 节和其他)作为一种特殊情况(例如,Serializable 是原始数组的有效超类型)。与手头的讨论无关,但无论如何都很有趣。
      • 没有java.util.Enumerator。你的意思是Enumeration
      • @Cowan,谢谢。我什至查看了 JLS,但设法错过了这些部分。 @Joachim,我做到了。更新了我的帖子。
      【解决方案6】:

      因为 for-each 被设计为如下所示:

      for each element of [some collection of elements]
      

      Iterator 不是 [some collection of elements]。一个数组和一个Iterable 是。

      【讨论】:

        猜你喜欢
        • 2017-02-09
        • 1970-01-01
        • 1970-01-01
        • 2016-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-07
        相关资源
        最近更新 更多