为什么Java的Iterator不是Iterable?
题
为什么 Iterator
接口未扩展 Iterable
?
这 iterator()
方法可以简单地返回 this
.
这是故意的还是只是 Java 设计者的疏忽?
能够将 for-each 循环与迭代器一起使用会很方便,如下所示:
for(Object o : someContainer.listSomeObjects()) {
....
}
在哪里 listSomeObjects()
返回一个迭代器。
解决方案
因为迭代器通常指向集合中的单个实例。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);
}
}
这应该打印集合两次 - 但根据你的方案,第二个循环总是会立即终止。
对于我的 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 循环,正如其他地方所指出的,枚举类似于迭代器而不是可迭代器。
因此,不要让 Iterator 实现 Iterable,而是更新 for 循环以接受其中任何一个。
干杯,
正如其他人指出的那样, 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) {
正如其他人所说,Iterable 可以被多次调用,每次调用都返回一个新的 Iterator;迭代器仅使用一次。因此它们是相关的,但有不同的目的。然而,令人沮丧的是,“compact for”方法仅适用于可迭代对象。
下面我将描述一种两全其美的方法——即使底层数据序列是一次性的,也返回一个 Iterable(以获得更好的语法)。
诀窍是返回实际触发工作的 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 的示例中,这意味着迭代数据库事务中的结果。
因此有各种警告,但在许多情况下这仍然是一个有用的习惯用法。
令人难以置信的是,目前还没有其他人给出这个答案。以下是如何“轻松”迭代 Iterator
通过使用新的 Java 8 Iterator.forEachRemaining()
方法:
Iterator<String> it = ...
it.forEachRemaining(System.out::println);
当然,有一个“更简单”的解决方案,可以直接与 foreach 循环一起使用,将 Iterator
在一个 Iterable
拉姆达:
for (String s : (Iterable<String>) () -> it)
System.out.println(s);
Iterator
是一个允许您迭代某些内容的接口。它是在某种集合中移动的实现。
Iterable
是一个函数式接口,表示某物包含可访问的迭代器。
在 Java8 中,这让生活变得非常简单......如果你有一个 Iterator
但需要一个 Iterable
你可以简单地做:
Iterator<T> someIterator;
Iterable<T> = ()->someIterator;
这也适用于 for 循环:
for (T item : ()->someIterator){
//doSomething with item
}
我也看到很多人这样做:
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() 这两个方法
如果您来这里寻找解决方法,您可以使用 迭代器Iterable. 。(适用于 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
我同意已接受的答案,但想添加我自己的解释。
迭代器表示遍历的状态,例如,您可以从迭代器中获取当前元素并向前移动到下一个元素。
Iterable代表一个可遍历的集合,它可以返回任意多个迭代器,每个迭代器代表它自己的遍历状态,一个迭代器可能指向第一个元素,而另一个迭代器可能指向第三个元素。
如果 Java for 循环同时接受 Iterator 和 Iterable,那就太好了。
为了简单起见,Iterator 和 Iterable 是两个不同的概念,Iterable 只是“我可以返回一个 Iterator”的简写。我认为你的代码应该是:
for(Object o : someContainer) {
}
与 someContainer 实例 SomeContainer extends Iterable<Object>
作为旁白: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());
}