改进的集合迭代器
Posted
技术标签:
【中文标题】改进的集合迭代器【英文标题】:improved collection Iterator 【发布时间】:2010-09-16 14:02:15 【问题描述】:就我个人而言,我发现 java.util.Iterator 提供的功能范围相当可悲。至少,我希望有如下方法:
peek() 返回下一个元素而不向前移动迭代器 previous() 返回前一个元素虽然还有很多其他的可能性,比如 first() 和 last()。
有谁知道这样的第 3 方迭代器是否存在?它可能需要实现为 java.util.Iterator 的装饰器,以便它可以与现有的 java 集合一起使用。理想情况下,它应该是“泛型感知”。
提前致谢, 唐
【问题讨论】:
【参考方案1】:您只需使用java.util.ListIterator
即可轻松获得previous()
。
Peek 很容易通过执行
来实现public <T> T peek(ListIterator<T> iter) throws NoSuchElementException
T obj = iter.next();
iter.previous();
return obj;
不幸的是,将它作为一种实用方法会更容易,因为每个集合类都实现了自己的迭代器。在MyListIterator
之类的某个接口上做一个包装器来获取每个集合的peek方法将是相当多的工作。
【讨论】:
【参考方案2】:Apache Commons Collections
Google Collections
【讨论】:
谢谢,但 AFAIK Apache commons 集合 仍然 尚未通用 这两个链接现在都失效了 修复了链接,谢谢。 Apache 现在也支持泛型,Google Collections 作为 guava 的一部分提供。 Google Commons 中的迭代器是否同时支持peek()
和previous()
? PeekingIterator
支持peek()
但不支持previous()
。【参考方案3】:
我认为这些没有实现的原因是因为它们对于某些集合来说并不重要,并且会产生很大的性能影响。我认为您可以很容易地为您关心的收藏品制作这项工作。
我也不喜欢 Java 迭代器在不移动它的情况下无法获取当前值(因此你不能轻易地编写基于值分支的代码,只需传递迭代器——你必须传递你现在拥有的价值也是如此)。
【讨论】:
【参考方案4】:泛型运算符没有实现这些功能是有充分理由的:它们并非适用于所有容器。典型的例子是一个代表一些外部数据输入的容器,比如一个被视为流的文件。每次您读取一个值时,您使用它并将指针向前移动,无论您是否愿意。如果你对泛型迭代器施加这些约束,那么你就会失去迭代器的泛型性。
如果您想要previous
方法,如建议的那样,请使用ListIterator<>
,然后限制容器表现为列表。
【讨论】:
文件参数并没有真正令人信服,我们没有理由不前进文件中的光标就无法获取当前字符(或我们正在阅读的任何内容)。但是,我同意previous
功能不能由每个迭代器提供(尤其是“生成器”迭代器,它在每一步都会生成一个新值)。
@Luc:这是因为您将文件视为随机访问。他们真的不必如此。如果您愿意,可以考虑使用网络套接字等流。您阅读收到的内容,如果您想提前阅读,则需要一个完整的缓存机制。所以如果你想让你的迭代器在所有情况下都能使用,你真的需要Java提供的接口。之后,您可以使用适配器,或更专业的迭代器。对于泛型迭代器来说,Java 提供的接口仍然是最明智的。
这不是随机访问的问题:即使在仅向前的流上,我也看不出为什么访问刚刚读取的值并读取下一个值应该被视为单个的原因手术。但我想这是相当主观的,我们必须同意不同意:)。但是,我想补充一点,我发现在一个应该非常通用的接口上使用 remove
方法有点奇怪,而删除是一种只能在一组相当有限的可迭代对象上进行的操作.【参考方案5】:
我要看的一件事是 clojure 中的 Seq 实现
http://clojure.org/sequences
基类的实现是用 Java 实现的,并且有完整的源代码可用。 Seq 是 Java 迭代器上的装饰器(采用并实现 Java 迭代器接口)——但它们也提供自己的接口,这可能是你想要的更多——或者至少是一个起点。
【讨论】:
【参考方案6】:我看到有人链接到Google Collections,但是没有人提到您要查找的方法称为Iterators.peekingIterator()。
不过,最好只使用 ListIterator。
【讨论】:
【参考方案7】:正如 ykaganovich 所建议的,您可能想查看 google-collections 的内容。你想要的一些东西肯定有一些支持,比如peeking。此外,正如其他一些人所提到的,从可能性或性能的角度来看,为所有集合实现所有这些东西可能是危险的。
【讨论】:
【参考方案8】: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();
【讨论】:
【参考方案9】:我从来没有遇到过需要 peek(); 的问题。迭代器对我来说工作得很好。我很好奇你是如何使用迭代器的,你觉得你需要这个附加的功能。
【讨论】:
我认为最常见的情况是某种调度。你想读取第一个元素,看看谁应该处理它并分派——如果接收者需要整个序列,那么只需传递它而不需要传递已删除的对象就很好。 我可以看到。它只是有一种气味,可以通过其他方式更容易完成,比如访客或其他东西。【参考方案10】:听起来你最好使用堆栈。
【讨论】:
【参考方案11】:编写 Java 集合是为了提供一组最小的有用功能。对于任何实现 Java 的人必须实现的代码,这是一个非常好的方法。用可能有用的功能使界面膨胀会导致代码量大幅增加,而只有少数人注意到了改进。如果 peek() 和 previous() 是标准迭代器的一部分,这意味着每个编写新集合的人必须实现它,无论它是否合理。
迭代器还被设计用于处理物理上不能倒退的事情,这使得 peek() 和 previous() 都无法实现。
【讨论】:
以上是关于改进的集合迭代器的主要内容,如果未能解决你的问题,请参考以下文章
上手并过渡到PHP7——轻量级“集合”迭代器-Generator
Java中定义一个迭代器(Iterator)遍历完一个集合后,如果还要再遍历一次这个集合?