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

时间:2009-05-08 10:19:43

标签: java iterator iterable

为什么Iterator界面不会延伸Iterable

iterator()方法只需返回this

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

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

for(Object o : someContainer.listSomeObjects()) {
    ....
}

其中listSomeObjects()返回迭代器。

16 个答案:

答案 0 :(得分:209)

迭代器是有状态的。我们的想法是,如果你两次调用Iterable.iterator(),你将获得独立的迭代器 - 无论如何,对于大多数迭代。在您的方案中显然不会出现这种情况。

例如,我通常可以写:

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

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

答案 1 :(得分:62)

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

答案 2 :(得分:55)

对于我的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) {
    ...
}

为什么这个相同的参数不适用于迭代器?

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

for (Item item : iter) {
    ...
}

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

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

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

干杯,

答案 3 :(得分:17)

正如其他人所指出的,IteratorIterable是两回事。

此外,Iterator实现早于针对循环的增强。

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

for (String line : in(lines)) {
  System.out.println(line);
}

示例实施:

  /**
   * 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();
  }

在Java 8中,将Iterator调整为Iterable变得更简单:

for (String s : (Iterable<String>) () -> iterator) {

答案 4 :(得分:8)

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

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

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

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

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();
            }
        };
    }
}

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

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

允许我使用compact for循环,同时仍然保持增量内存使用。

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

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

答案 5 :(得分:6)

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

Iterator<String> it = ...
it.forEachRemaining(System.out::println);

当然,有一个“更简单”的解决方案可以直接使用foreach循环,将Iterator包装在Iterable lambda中:

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);

答案 6 :(得分:5)

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

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

在Java8中,这让生活变得轻松......如果你有一个Iterator但需要一个Iterable,你可以做到:

Iterator<T> someIterator;
Iterable<T> = ()->someIterator;

这也适用于for循环:

for (T item : ()->someIterator){
    //doSomething with item
}

答案 7 :(得分:2)

我也看到很多人这样做:

public Iterator iterator() {
    return this;
}

但这不是正确的! 这种方法不是你想要的!

方法iterator()应该从头开始返回一个新的迭代器。 所以需要做这样的事情:

public class IterableIterator implements Iterator, Iterable {

  //Constructor
  IterableIterator(IterableIterator iter)
  {
    this.initdata = iter.initdata;
  }
  // methods of Iterable

  public Iterator iterator() {
    return new MyClass(this.somedata);
  }

  // methods of Iterator

  public boolean hasNext() {
    // ...
  }

  public Object next() {
    // ...
  }

  public void remove() {
    // ...
  }
}

问题是:是否有任何方法可以使抽象类执行此操作? 因此,要获得IterableIterator只需要实现next()和hasNext()

这两个方法

答案 8 :(得分:2)

为避免依赖java.util软件包

根据原始JSR An enhanced for loop for the Java™ Programming Language,建议的接口:

  • java.lang.Iterable
  • java.lang.ReadOnlyIterator
    (建议改装到java.util.Iterator上,但显然这从未发生过)

…被设计为使用java.lang软件包名称空间而不是java.util

引用JSR:

  

这些新接口用于防止语言对java.util的依赖,否则将导致这种依赖。


顺便说一句,旧的java.util.Iterable在Java 8+中获得了新的forEach方法,用于lambda语法(通过Consumer)。

这里是一个例子。 List接口扩展了Iterable接口,因为任何列表都带有forEach方法。

List
.of ( "dog" , "cat" , "bird" )
.forEach ( ( String animal ) -> System.out.println ( "animal = " + animal ) );

答案 9 :(得分:1)

如果您来这里寻找解决方法,可以使用IteratorIterable。 (适用于Java 1.6及更高版本)

示例用法(反转Vector)。

import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
    public static void main(String ... args) {
        Vector<String> vs = new Vector<String>();
        vs.add("one");
        vs.add("two");
        for ( String s: vs ) {
            System.out.println(s);
        }
        Iterable<String> is
            = new IteratorIterable(new ReverseListIterator(vs));
        for ( String s: is ) {
            System.out.println(s);
        }
    }
}

打印

one
two
two
one

答案 10 :(得分:1)

我同意接受的答案,但想加上我自己的解释。

  • Iterator表示遍历的状态,例如,您可以从迭代器获取当前元素并继续前进到下一个。

  • Iterable表示可以遍历的集合,它可以返回任意数量的迭代器,每个迭代器表示自己的遍历状态,一个迭代器可能指向第一个元素,而另一个可能指向第3个元素。

如果Java for循环同时接受Iterator和Iterable,那将是很好的。

答案 11 :(得分:0)

为简单起见,Iterator和Iterable是两个截然不同的概念,Iterable只是“我可以返回Iterator”的简写。我认为你的代码应该是:

for(Object o : someContainer) {
}

with someContainer instanceof SomeContainer extends Iterable<Object>

答案 12 :(得分:0)

暂且不说:Scala在Iterator中有一个toIterable()方法。见scala implicit or explicit conversion from iterator to iterable

答案 13 :(得分:0)

在相关的说明中,您可能会发现Apache Commons Collections4中的IteratorIterable适配器很有用。只需从迭代器创建一个实例,就可以得到相应的迭代。

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html

ID:org.apache.commons:commons-collections4:4.0

答案 14 :(得分:0)

迭代器是有状态的,他们有一个&#34; next&#34;元素并且变得疲惫不堪&#34;曾经迭代过。要查看问题所在,请运行以下代码,打印多少个数字?

Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);

答案 15 :(得分:-1)

您可以尝试以下示例:

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}