为什么Iterator
接口不扩展Iterable
?
该iterator()
方法可以简单地返回this
。
是故意的还是对Java设计人员的监督?
能够将for-each循环与这样的迭代器一起使用会很方便:
for(Object o : someContainer.listSomeObjects()) {
....
}
其中listSomeObjects()
返回迭代器。
为什么Iterator
接口不扩展Iterable
?
该iterator()
方法可以简单地返回this
。
是故意的还是对Java设计人员的监督?
能够将for-each循环与这样的迭代器一起使用会很方便:
for(Object o : someContainer.listSomeObjects()) {
....
}
其中listSomeObjects()
返回迭代器。
Answers:
因为迭代器通常指向集合中的单个实例。Iterable意味着可以从一个对象获得迭代器以遍历其元素-无需迭代单个实例(迭代器表示的是)。
迭代器是有状态的。这个想法是,如果调用Iterable.iterator()
两次,您将获得独立的迭代器-无论如何对于大多数可迭代对象。在您的方案中显然不是这样。
例如,我通常可以这样写:
public void iterateOver(Iterable<String> strings)
{
for (String x : strings)
{
System.out.println(x);
}
for (String x : strings)
{
System.out.println(x);
}
}
这应该将集合打印两次-但是根据您的方案,第二个循环将始终立即终止。
iterator
并使用结果,则必须在集合上进行迭代-如果相同的对象已经在集合上进行迭代,则不会这样做。您能否给出相同的迭代器两次返回的任何正确实现(对于空集合除外)?
iterable
两次将为您提供独立的迭代器。
对于我的$ 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循环之外:在循环内,每次迭代器返回的项都只能向前推进一个。
同样,允许这样做也将使for循环易于用于枚举,正如其他地方所指出的,它类似于Iterators not Iterables。
因此,不要使Iterator实现Iterable,而是更新for循环以接受其中任何一个。
干杯,
for(item : iter) {...}
语法在迭代器上促进迭代,则当同一迭代器被迭代两次时,它将引发错误。想象一下,Iterator
被传递到iterateOver
方法,而不是Iterable
在这个例子。
for (String x : strings) {...}
or还是while (strings.hasNext()) {...}
style 都没有关系:如果第二次尝试遍历迭代器两次将不会产生任何结果,因此我不认为它本身就是反对允许使用增强语法的论点。乔恩的答案是不同的,因为他展示了如何包装一个Iterator
在Iterable
会造成问题,因为你会希望能够重复使用多次,你喜欢的话。
正如其他人指出的,Iterator
和Iterable
是两个不同的事物。
而且,Iterator
实现早于for循环的实现。
克服以下限制也是很简单的,当与静态方法导入一起使用时,使用如下所示的简单适配器方法即可:
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) {
for (String s : (Iterable<String>) () -> iterator)
正如其他人所说的,一个Iterable可以被多次调用,每次调用都返回一个新的Iterator。迭代器仅使用一次。因此它们是相关的,但有不同的用途。但是,令人沮丧的是,“ compact for”方法仅适用于可迭代的方法。
我将在下面描述的是一种兼具两全其美的方法-即使数据的基础序列是一次性的,也要返回Iterable(用于更好的语法)。
诀窍是返回实际触发工作的Iterable的匿名实现。因此,您无需执行生成一次性序列的工作,然后在该序列上返回Iterator的方法,而是返回一个Iterable,每次访问该Iterable都会重做该工作。这看起来很浪费,但是无论如何,您通常只会调用一次Iterable,即使多次调用它,它仍然具有合理的语义(不同于使Iterator看起来像Iterable的简单包装器,这不会如果使用两次则失败。
例如,假设我有一个DAO,它从数据库中提供了一系列对象,并且我想通过迭代器提供对该对象的访问(例如,避免在不需要的情况下在内存中创建所有对象)。现在我可以返回一个迭代器,但是这使得在循环中使用返回值很丑陋。因此,我将所有内容包装在一个匿名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();
}
};
}
}
然后可以在如下代码中使用它:
class DaoUser {
private MetricDao dao;
for (Metric existing : dao.loadAll()) {
// do stuff here...
}
}
这使我可以使用紧凑的for循环,同时仍然保持增量内存使用。
这种方法是“懒惰的”-请求Iterable时不完成工作,而仅在内容进行迭代时才进行-您需要意识到这样做的后果。在具有DAO的示例中,这意味着对数据库事务中的结果进行迭代。
因此有很多警告,但是在许多情况下这仍然是一个有用的习惯用法。
returning a fresh Iterator on each call
应该伴随着为什么,即防止并发问题……
令人难以置信的是,没有人给出这个答案。这是Iterator
使用新的Java 8 Iterator.forEachRemaining()
方法“轻松”遍历的方法:
Iterator<String> it = ...
it.forEachRemaining(System.out::println);
当然,有一个“更简单”的解决方案可以直接与foreach循环一起使用,将其包装Iterator
在一个Iterable
lambda中:
for (String s : (Iterable<String>) () -> it)
System.out.println(s);
java.util
包装根据原始的JSR(Java™编程语言的增强的for循环),建议的接口:
java.lang.Iterable
java.lang.ReadOnlyIterator
java.util.Iterator
,但显然这从未发生过)…被设计为使用java.lang
包名称空间而不是java.util
。
引用JSR:
这些新接口用于防止语言对java.util的依赖,否则将导致这种依赖。
顺便说一句,旧版本在Java 8+中java.util.Iterable
获得了一种新forEach
方法,可用于lambda语法(通过Consumer
)。
这是一个例子。的List
接口扩展Iterable
接口,如任何列表中携带一个forEach
方法。
List
.of ( "dog" , "cat" , "bird" )
.forEach ( ( String animal ) -> System.out.println ( "animal = " + animal ) );
我也看到很多这样做:
public Iterator iterator() {
return this;
}
但这并不正确!这种方法不是您想要的!
该方法iterator()
应该从头开始返回一个新的迭代器。因此,需要执行以下操作:
public class IterableIterator implements Iterator, Iterable {
//Constructor
IterableIterator(SomeType initdata)
{
this.initdata = iter.initdata;
}
// methods of Iterable
public Iterator iterator() {
return new IterableIterator(this.intidata);
}
// methods of Iterator
public boolean hasNext() {
// ...
}
public Object next() {
// ...
}
public void remove() {
// ...
}
}
问题是:有没有办法使抽象类执行此操作?因此,要获得IterableIterator,只需实现next()和hasNext()这两种方法
如果您是来这里寻找解决方法的,则可以使用IteratorIterable。(适用于Java 1.6及更高版本)
用法示例(反转向量)。
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
顺便说一句:Scala在Iterator中有一个toIterable()方法。查看从迭代器到可迭代的scala隐式或显式转换
在相关说明中,您可能会发现Apache Commons Collections4中的IteratorIterable适配器很有用。只需从迭代器创建实例,即可获得相应的可迭代器。
ID:org.apache.commons:commons-collections4:4.0
迭代器是有状态的,它们具有“下一个”元素,并且一旦迭代结束就变得“精疲力竭”。要查看问题出在哪里,请运行以下代码,打印多少个数字?
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);
您可以尝试以下示例:
List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
System.out.println(iterator.next());
}