我刚刚了解了Java Collections Framework如何在链表中实现数据结构。据我所知,Iterators
是遍历数据结构(如列表)中项目的一种方式。为什么使用这个界面?为什么方法hasNext()
,next()
和remove()
没有直接编码到数据结构实现本身?
来自Java网站:link text
public interface Iterator< E>
An 集合上的迭代器。迭代器 取代了Enumeration Java集合框架。迭代器 与枚举有两点不同:
这个界面是 Java Collections的成员 框架。
- 迭代器允许调用者删除 来自底层的元素 迭代期间的集合 明确定义的语义。
- 方法名称 已经改进了。
我试着用Google搜索,似乎找不到明确的答案。有人可以解释为什么Sun选择使用它们吗?是因为更好的设计?增加安全性?良好的OO实践?
任何帮助将不胜感激。感谢。
答案 0 :(得分:18)
为什么使用此界面?
因为它支持允许客户端程序员迭代任何类型集合的基本操作(注意:Collection
意义上不一定是Object
。
为什么方法......不是直接的 编码到数据结构 实施本身?
他们是,他们只是标记为私人,所以你无法接触他们并与他们捣乱。更具体地说:
Iterator
,使其执行标准不执行的操作,而不必更改迭代的实际对象。Iterators
分发给您想要的许多客户,并且每个客户可以按照自己的速度在自己的时间内进行遍历。Iterators
特别会在您仍然有Iterator
输出的情况下修改支持它们的存储时抛出异常。此异常会让您知道Iterator
现在可能正在返回无效对象。对于简单的程序,这似乎都不值得。然而,那种使它们变得有用的复杂性很快就会出现在你身上。
答案 1 :(得分:6)
你问:“为什么方法hasNext(),next()和remove()没有直接编码到数据结构实现本身?”。
Java Collections框架选择将Iterator接口定义为集合本身外部化。通常,由于每个Java集合都实现Iterable
接口,因此Java程序将调用iterator
来创建自己的迭代器,以便可以在循环中使用它。正如其他人所指出的那样,Java 5允许我们使用for-each循环来直接使用迭代器。
将迭代器外部化到其集合允许客户端控制迭代集合的方式。我可以想到一个有用的用例是当一个人有一个无界的集合,比如因特网上的所有网页都要编制索引。
在经典的GoF书中,内部和外部迭代器之间的对比非常明确。
一个基本问题是决定哪一方控制迭代,迭代器或使用迭代器的客户端。当客户端控制迭代时,迭代器被称为外部迭代器,当迭代器控制它时,迭代器是一个内部迭代器。使用外部迭代器的客户端必须推进遍历并从迭代器显式请求下一个元素。相反,客户端向内部迭代器提供要执行的操作,迭代器将该操作应用于每个元素....
外部迭代器比内部迭代器更灵活。例如,将两个集合与外部迭代器进行相等比较很容易,但内部迭代器实际上是不可能的......但另一方面,内部迭代器更容易使用,因为它们为您定义了迭代逻辑。 p>
有关内部迭代器如何工作的示例,请参阅Ruby的Enumerable
API,它具有内部迭代方法,例如each
。在Ruby中,我们的想法是将一个代码块(即一个闭包)传递给内部迭代器,以便集合可以处理它自己的迭代。
答案 2 :(得分:4)
将集合与指针分开是很重要的。迭代器指向集合中的特定位置,因此不是集合的组成部分。这样,对于一个实例,您可以在同一个集合中使用多个迭代器。
这个分离的缺点是迭代器不知道对它迭代的集合所做的更改。所以你不能改变集合的结构,并期望迭代器在没有“抱怨”的情况下继续它的工作。
答案 3 :(得分:3)
使用Iterator
接口允许任何实现其方法的类充当迭代器。 Java中接口的概念在某种程度上具有契约义务,即在implements
接口的类中提供某些功能,以接口所需的方式执行。由于必须满足合同义务才能成为一个有效的类,其他类看到类implements
接口,因此可以放心地知道该类将具有这些特定的功能。
在此示例中,hasNext(), next(), remove()
类不会在LinkedList
类本身中实现方法(LinkedList
),而是声明它implements
Iterator
接口,所以其他人知道LinkedList
可以用作迭代器。反过来,LinkedList
类将实现Iterator
接口中的方法(例如hasNext()
),因此它可以像迭代器一样运行。
换句话说,实现一个接口是一个面向对象的编程概念,让其他人知道某个类具有它所声称的所需要的东西。
这个概念是通过具有必须由实现接口的类实现的方法来实施的。这确保了其他想要使用实现Iterator
接口的类的类,它确实具有迭代器应具有的方法,例如hasNext()
。
此外,应该注意的是,由于Java没有多重继承,因此可以使用接口来模拟该功能。通过实现多个接口,可以拥有一个继承某些功能的子类,还可以通过实现接口“继承”另一个功能。一个例子是,如果我想要一个名为LinkedList
的{{1}}类的子类可以以相反的顺序迭代,我可以创建一个名为ReversibleLinkedList
的接口并强制它提供一个ReverseIterator
方法。由于previous()
已经实现了LinkedList
,新的可逆列表将同时实现Iterator
和Iterator
接口。
您可以从Sun的Java教程中了解What is an Interface?中的接口的更多信息。
答案 4 :(得分:3)
可以同时使用多个interator实例。将它们作为基础数据的本地游标进行处理。
BTW:在具体实现上偏向接口会失去耦合寻找迭代器设计模式,在这里:http://en.wikipedia.org/wiki/Iterator
答案 5 :(得分:2)
因为您可能正在迭代不是数据结构的东西。假设我有一个联网应用程序从服务器提取结果。我可以通过接受Iterator对象的任何标准代码返回围绕这些结果的Iterator包装器和流式传输。
将其视为良好MVC设计的关键部分。数据必须以某种方式从模型(即数据结构)到视图。使用迭代器作为中间人可确保永远不会暴露模型的实现。您可以将LinkedList保留在内存中,从解密算法中提取信息,或者包装JDBC调用。它与视图无关,因为视图只关心Iterator接口。
答案 6 :(得分:1)
一篇有趣的论文讨论了使用迭代器的优缺点:
答案 7 :(得分:1)
我认为这只是优秀的OO实践。您可以拥有处理各种迭代器的代码,甚至可以让您有机会创建自己的数据结构或只是实现迭代器接口的泛型类。您不必担心它背后的实施方式。
答案 8 :(得分:1)
只是M2C,如果您不知道:在for-each循环就足够的情况下,您可以避免直接使用迭代器接口。
答案 9 :(得分:1)
最终,因为Iterator捕获适用于大量数据结构的控件抽象。如果你对你的类别理论有所了解,你可以通过本文讨论你的想法:The Essence of the Iterator Pattern。
答案 10 :(得分:0)
好吧,似乎第一个要点允许多线程(或单线程,如果你搞砸了)应用程序不需要锁定集合以进行并发冲突。例如,在.NET中,您无法同时枚举和修改集合(或列表或任何IEnumerable),而无需锁定或继承IEnumerable和重写方法(我们会获得异常)。
答案 11 :(得分:0)
Iterator简单地添加了一种遍历项目集合的常用方法。其中一个不错的功能是i.remove(),您可以从中删除正在迭代的列表中的元素。如果您只是试图从列表中删除项目,通常会产生奇怪的效果或抛出异常。
界面就像是实现它的所有事物的契约。你基本上是在说..实现迭代器的任何东西都保证这些方法的行为方式相同。您也可以使用它来传递迭代器类型,如果这是您在代码中处理的全部内容。 (你可能不关心它是什么类型的列表..你只想传递一个迭代器)你可以将所有这些方法独立地放在集合中,但你不能保证它们的行为相同或者它们甚至具有相同的名称和签名。
答案 12 :(得分:0)
迭代器是java中可用的众多设计模式之一。设计模式可以被认为是方便的构建块,样式,代码/结构的使用。
要了解有关Iterator设计模式的更多信息,请查看本网站,其中介绍了Iterator以及许多其他设计模式。以下是Iterator网站上的代码段:http://www.patterndepot.com/put/8/Behavioral.html
Iterator是最简单的之一 最常用的设计 图案。 Iterator模式允许 你要通过列表或 使用标准收集数据 界面,而不必知道 内部细节 该数据的表示。在 另外你还可以定义特殊的 执行一些特殊的迭代器 处理和返回仅指定 数据集的元素。
答案 13 :(得分:0)
迭代器可用于任何类型的集合。它们允许您针对项集合定义算法,而不管底层实现如何。这意味着您可以处理List,Set,String,File,Array等。
十年后,您可以将List实现更改为更好的实现,算法仍然可以无缝地运行。
答案 14 :(得分:0)
当您使用Java处理集合时,Iterator非常有用。
使用 For-Each 循环(Java1.5)迭代集合或数组或列表。
答案 15 :(得分:0)
Java Collections Framework中使用了java.util.Iterator接口,允许在仍然迭代它的同时修改集合。如果你只想干净地遍历整个集合,请使用for-each,但Iterators的优点是你得到的功能:一个可选的remove()操作,甚至更好的List Iterator接口,它提供了add ()和set()操作也是如此。这两个接口都允许您迭代集合并同时在结构上进行更改。尝试在使用for-each迭代它时修改集合会抛出ConcurrentModificationException,通常是因为集合被意外修改了!
看一下ArrayList类
里面有2个私有类(内部类) 叫做Itr和ListItr
分别实现Iterator和ListIterator接口
public class ArrayList ..... {//封闭类
private class Itr implements Iterator<E> {
public E next() {
return ArrayList.this.get(index++); //rough, not exact
}
//we have to use ArrayList.this.get() so the compiler will
//know that we are referring to the methods in the
//enclosing ArrayList class
public void remove() {
ArrayList.this.remove(prevIndex);
}
//checks for...co mod of the list
final void checkForComodification() { //ListItr gets this method as well
if (ArrayList.this.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
}
private class ListItr extends Itr implements ListIterator<E> {
//methods inherted....
public void add(E e) {
ArrayList.this.add(cursor, e);
}
public void set(E e) {
ArrayList.this.set(cursor, e);
}
}
}
当你调用方法iterator()和listIterator()时,它们会返回 私有类Itr或ListItr的新实例,并且由于这些内部类在封闭的ArrayList类中“,”它们可以自由地修改ArrayList而不会触发ConcurrentModificationException,除非您同时(通过set)同时更改列表()ArrayList类的add()或remove()方法。