改进的集合迭代器

13

个人而言,我认为 java.util.Iterator 提供的功能范围相当有限。至少,我希望有以下方法:

  • peek() 返回下一个元素但不移动迭代器
  • previous() 返回上一个元素

当然还有许多其他可能性,例如 first() 和 last()。

是否有人知道这样的第三方迭代器存在吗?它可能需要作为 java.util.Iterator 的装饰器实现,以便与现有的 Java 集合一起工作。最好是具有“泛型意识”。

提前感谢, 唐

11个回答

11

您可以通过使用 java.util.ListIterator 轻松获取 previous()

在那一点上进行查看可以通过执行以下操作轻松实现

public <T> T peek(ListIterator<T> iter) throws NoSuchElementException {
    T obj = iter.next();
    iter.previous();
    return obj;
}

很不幸,由于每个集合类都实现了自己的迭代器,因此将其作为实用程序方法会更容易。要在某些接口(如MyListIterator)上包装以获取每个集合的peek方法需要相当大的工作量。


8

我认为这些没有被实现的原因是它们对某些集合而言并不是简单的事情,并且会有较大的性能影响。但我认为对于那些你关心的集合,让这个工作起来应该很简单。

另外我也不喜欢Java迭代器没有办法获取当前值(因此无法根据值编写分支代码,只传递迭代器--你还必须传递你现在拥有的值)。


8

1
谢谢,但据我所知,Apache Commons Collections仍未进行泛型化。 - Dónal
1
这两个链接现在都失效了。 - Quince
链接已修复,谢谢。Apache现在也支持泛型,并且Google Collections作为guava的一部分可用。 - ykaganovich
Google Commons 中的任何迭代器都支持 peek()previous() 吗?PeekingIterator 支持 peek() 但不支持 previous() - Phil

4

通常情况下,泛型运算符不会实现这些功能,因为并非所有容器都具备这些功能。一个典型的例子是表示某些外部数据输入的容器,例如将文件视为流。每次读取值时,您都会消耗它并将指针向前移动,无论您是否需要。如果您将这些约束强加于泛型迭代器,则会失去迭代器的泛型性。

如果您想要一个“previous”方法(如建议的),请使用ListIterator<>,这将限制为行为类似于列表的容器。


文件参数并不是非常令人信服,我们没有理由无法在不推进文件游标的情况下获取当前字符(或者我们正在读取的任何内容)。但是,我同意“previous”功能不能由每个迭代器提供(特别是“生成器”迭代器,在每一步生成新值)。 - Luc Touraille
@Luc:这是因为您将文件视为随机访问。实际上它们并不需要是这样的。如果您喜欢,可以考虑使用流,例如网络套接字。您读取接收到的内容,如果想要预读,就需要一个完整的缓存机制。因此,如果您希望您的迭代器在所有情况下都能够使用,您确实需要Java提供的接口。之后,您可以使用适配器或更专业的迭代器。对于通用迭代器来说,Java提供的接口是最明智的选择。 - PierreBdR
这不是随机访问的问题:即使在仅向前流动的流中,我也无法看到为什么访问刚读取的值并读取下一个值应被视为单个操作。但我猜这是相当主观的,我们必须同意不同的看法 :)。然而,我想补充说,当删除是一种仅能在相当有限的可迭代对象集合上执行的操作时,我发现在一个被认为是非常通用的接口上有一个“删除”方法有点奇怪。 - Luc Touraille

3

我建议您查看Clojure中的Seq实现。

http://clojure.org/sequences

这些基础类的实现是用Java编写的,完整的源代码也是可用的。 Seq是Java迭代器上的装饰器(接收并实现Java迭代器接口)- 但它们还提供了自己的接口,这可能更符合您的需求 - 或者至少是一个起点。


2
我看到有人链接了Google Collections,但没有人提到你正在寻找的方法叫做Iterators.peekingIterator()。
不过,最好还是使用ListIterator。

1
正如ykaganovich所建议的那样,您可能想要查看google-collections的内容。它确实支持您想要的一些功能,例如peeking。此外,正如其他一些人所提到的,为所有集合实现所有这些功能可能从可能性或性能的角度来看是危险的。

1
public class Iterazor<T> {
  private Iterator<T> it;
  public T top;
  public Iterazor(Collection<T> co) {
    this.it = co.iterator(); 
    top = it.hasNext()? it.next(): null; 
  }
  public void advance() { 
    top = it.hasNext()? it.next(): null; 
  }
}

// usage

for(Iterazor<MyObject> iz = new Iterazor<MyObject>(MyCollection); 
    iz.top!=null; iz.advance())
  iz.top.doStuff();
}

0
Java集合是为提供一组最小有用的功能而编写的。对于任何实现Java的人来说,这是一个非常好的方法。将接口与“可能”有用的功能膨胀会导致代码量显著增加,但只有少数人会注意到改进。如果peek()和previous()是标准迭代器的一部分,这意味着每个编写新类型的集合的人都必须实现它,无论是否明智。
迭代器还设计用于处理物理上无法向后移动的内容,因此peek()和previous()都是不可能的。

0

我从未遇到过需要使用peek()的问题;迭代器已经完全够用了。我很好奇您如何使用迭代器,以至于您感觉需要这个额外的功能。


我认为最常见的情况是某种调度。您想要读取第一个元素以查看谁应该处理它并进行调度 - 如果接收者需要整个序列,则仅传递该序列很好,而不必还要传递已删除的对象。 - Lou Franco
我能理解。它有一种可以用其他更简单的方式完成,比如使用访问者模式之类的东西的味道。 - Steve g

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接