使用给定迭代器的每个循环的惯用方式?

2022-09-01 07:24:33

当增强的 for 循环(foreach 循环)添加到 Java 中时,它被用作数组或 .Iterable

for ( T item : /*T[] or Iterable<? extends T>*/ ) {
    //use item
}

这对于仅实现一种类型的迭代,因此具有单个方法的集合类非常有用。iterator()

但是我发现自己非常沮丧,因为我想使用来自集合类的非标准迭代器。例如,我最近试图帮助某人将a用作LIFO /堆栈,然后按FIFO顺序打印元素。我被迫这样做:Deque

for (Iterator<T> it = myDeque.descendingIterator(); it.hasNext(); ) {
   T item = it.next();
   //use item
}

我失去了 for-each 循环的优势。这不仅仅是击键。如果没有必要,我不喜欢公开迭代器,因为很容易犯调用两次的错误,等等。it.next()

现在理想情况下,我认为 for-each 循环也应该接受一个。但事实并非如此。那么,在这些情况下,是否有一种惯用的方法可以使用for-each循环呢?我也想听听使用番石榴等常见馆藏库的建议。Iterator

在帮助器方法/类之外,我能想到的最好的是:

for ( T item : new Iterable<T>() { public Iterator<T> iterator() { return myDeque.descendingIterator(); } } ) {
    //use item
}

这是不值得使用的。

我很想看到番石榴有类似的东西来做这个习语,但没有找到这样的东西。显然,我可以通过类或帮助器方法滚动我自己的迭代器包装器。还有其他想法吗?Iterables.wrap

编辑:作为旁注,任何人都可以给出一个有效的理由来解释为什么增强的for循环不应该只接受一个?这可能对让我接受当前的设计有很长的路要走。Iterator


答案 1

为什么增强的 for 循环不只接受迭代器?

我想从各种答案中收集一些潜在的原因,说明为什么for-each循环不简单地接受迭代器。

  1. 便利性:创建 for-each 循环的部分原因是为了方便在给定集合的每个元素的情况下执行操作的常见操作。它没有义务或意图替换迭代器的显式使用(显然,如果你想删除元素,你需要一个对迭代器的显式引用)。
  2. 可读性:for-each 循环意味着非常可读,因为“对于表中的每一行'r'...”。看到打破了可读性。for ( Row r : table )for ( Row r : table.backwardsIterator() )
  3. 透明度:如果一个对象既是 an 是 a,那么行为会是什么?虽然制定一致的规则很容易(例如,在迭代器之前可迭代),但行为对开发人员来说将不那么透明。此外,这必须在编译时进行检查。IterableIterator
  4. 封装/范围:这是(在我看来)最重要的原因。for-each 循环旨在封装 ,并将其范围限制在循环中。这使得循环在两个方面是“只读的”:它不会公开迭代器,这意味着没有任何(容易)有形的东西循环改变了它的状态,也不能改变循环操作数的状态(就像你可以通过直接与迭代器连接一样)。自己传递迭代器必然意味着迭代器被公开,使你失去循环的这两个“只读”属性。Iteratorremove()

答案 2

我可能会做的是创建一个可以支持此功能的实用程序类,如果需要,还可以与其他实用程序一起支持它。Deques

public class Deques {
  private Deques() {}

  public static <T> Iterable<T> asDescendingIterable(final Deque<T> deque) {
    return new Iterable<T>() {
      public Iterator<T> iterator() {
        return deque.descendingIterator();
      }
    }
  }
}

这是另一种情况,我们真的太糟糕了,我们还没有lambda和方法引用。在 Java 8 中,您将能够编写类似这样的东西,前提是方法引用与以下签名匹配:descendingIterator()Iterable

Deque<String> deque = ...
for (String s : (Iterable<String>) deque::descendingIterator) { ... }