36

我刚刚了解了 Java 集合框架如何在链表中实现数据结构。据我了解,Iterators这是一种遍历数据结构(例如列表)中的项目的方法。为什么要使用这个接口?为什么是方法hasNext()next()remove()不是直接编码到数据结构实现本身?

来自 Java 网站:链接文本

公共接口迭代器<E>

集合上的迭代器。Iterator 在 Java 集合框架中取代了 Enumeration。迭代器在两个方面不同于枚举:

  • 迭代器允许调用者在具有明确定义的语义的迭代期间从底层集合中删除元素。
  • 方法名称已得到改进。
此接口是 Java 集合框架的成员。

我尝试谷歌搜索,似乎无法找到明确的答案。有人能解释一下 Sun 选择使用它们的原因吗?是因为更好的设计吗?提高安全性?好的OO实践?

任何帮助将不胜感激。谢谢。

4

16 回答 16

18

为什么要使用这个接口?

Collection因为它支持允许客户端程序员迭代任何类型的集合的基本操作(注意:在Object某种意义上不一定是 a )。

为什么方法......不直接编码到数据结构实现本身?

他们是,他们只是被标记为私人,所以你不能接触到他们并与他们混为一谈。进一步来说:

  • 您可以实现或子类化 an Iterator,使其执行标准对象不执行的操作,而无需更改其迭代的实际对象。
  • 可以遍历的对象不需要让它们的接口被遍历方法弄得一团糟,尤其是任何高度专业化的方法。
  • 您可以分Iterators发给您希望的任意数量的客户,并且每个客户都可以在自己的时间以自己的速度穿越。
  • 特别是 java.util 包中的JavaIterators如果在您仍然有输出的情况下修改了支持它们的存储,则会引发异常Iterator。此异常让您知道Iterator现在可能正在返回无效对象。

对于简单的程序,这似乎都不值得。不过,使它们有用的那种复杂性很快就会出现在你身上。

于 2008-09-18T05:06:50.500 回答
6

你问:“为什么方法 hasNext()、next() 和 remove() 不直接编码到数据结构实现本身?”。

Java Collections 框架选择将 Iterator 接口定义为集合本身的外部化。通常,由于每个 Java 集合都实现了该Iterable接口,因此 Java 程序将调用iterator以创建自己的迭代器,以便可以在循环中使用它。正如其他人所指出的,Java 5 允许我们使用 for-each 循环直接使用迭代器。

将迭代器外部化到它的集合允许客户端控制如何迭代一个集合。我能想到的一个有用的用例是当一个人有一个无限的集合,例如要索引的 Internet 上的所有网页。

在经典的 GoF 书中,内部迭代器和外部迭代器之间的对比非常清晰。

一个基本问题是决定哪一方控制迭代,迭代器或使用迭代器的客户端。当客户端控制迭代时,迭代器称为外部迭代器,当迭代器控制它时,迭代器称为内部迭代器。使用外部迭代器的客户端必须推进遍历并从迭代器显式请求下一个元素。相反,客户端将一个操作交给内部迭代器执行,而迭代器将该操作应用于每个元素......

外部迭代器比内部迭代器更灵活。例如,用外部迭代器比较两个集合的相等性很容易,但使用内部迭代器实际上是不可能的……但另一方面,内部迭代器更易于使用,因为它们为您定义了迭代逻辑。

有关内部迭代器如何工作的示例,请参阅 Ruby 的EnumerableAPI,它具有内部迭代方法,例如each. 在 Ruby 中,想法是将代码块(即闭包)传递给内部迭代器,以便集合可以处理自己的迭代。

于 2008-09-18T05:22:18.950 回答
5

将集合与指针分开很重要。迭代器指向集合中的特定位置,因此不是集合的组成部分。这样,例如,您可以在同一个集合上使用多个迭代器。

这种分离的缺点是迭代器不知道对其迭代的集合所做的更改。所以你不能改变集合的结构并期望迭代器在没有“抱怨”的情况下继续它的工作。

于 2008-09-18T09:33:25.133 回答
3

使用该Iterator接口允许任何实现其方法的类充当迭代器。Java 中接口的概念在某种程度上具有合同义务,即implements在接口的类中提供某些功能,以接口所需的方式进行操作。由于必须满足合同义务才能成为有效的类,因此其他类看到该类implements的接口并因此放心知道该类将具有那些某些功能。

在这个例子中,类将声明它是接口,而不是在类本身中实现方法( hasNext(), next(), remove()),所以其他人知道可以用作迭代器。反过来,该类将实现接口中的方法(例如),因此它可以像迭代器一样工作。LinkedListLinkedListimplementsIteratorLinkedListLinkedListIteratorhasNext()

换句话说,实现一个接口是一个面向对象的编程概念,让其他人知道某个类具有它所声称的样子。

这个概念是通过具有必须由实现接口的类实现的方法来实施的。这确保了其他想要使用实现Iterator接口的类的类确实具有迭代器应该具有的方法,例如hasNext().

另外需要注意的是,由于 Java 没有多重继承,所以可以使用接口来模拟该特性。通过实现多个接口,可以拥有一个作为子类的类来继承某些特性,同时也可以通过实现一个接口来“继承”另一个的特性。一个例子是,如果我想要一个LinkedList被调用的类的子类,ReversibleLinkedList它可以以相反的顺序迭代,我可以创建一个被调用的接口ReverseIterator并强制它提供一个previous()方法。由于LinkedList已经实现Iterator,新的可逆列表将同时实现IteratorReverseIterator接口。

您可以从什么是接口?摘自 Sun 的 Java 教程。

于 2008-09-18T04:39:34.763 回答
3

可以同时使用一个交互器的多个实例。将它们作为基础数据的本地游标来处理。

顺便说一句:偏爱接口而不是具体实现会松散耦合

寻找迭代器设计模式,在这里:http ://en.wikipedia.org/wiki/Iterator

于 2008-09-18T05:32:46.413 回答
2

因为您可能正在迭代不是数据结构的东西。假设我有一个从服务器提取结果的联网应用程序。我可以返回围绕这些结果的 Iterator 包装器,并通过任何接受 Iterator 对象的标准代码将它们流式传输。

将其视为良好 MVC 设计的关键部分。数据必须以某种方式从模型(即数据结构)到视图。使用迭代器作为中间人确保模型的实现永远不会暴露。您可以将 LinkedList 保存在内存中,从解密算法中提取信息,或者包装 JDBC 调用。它对视图根本不重要,因为视图只关心 Iterator 接口。

于 2008-09-18T04:12:46.887 回答
1

一篇有趣的论文讨论了使用迭代器的利弊:

http://www.sei.cmu.edu/pacc/CBSE5/Sridhar-cbse5-final.pdf

于 2008-09-18T04:10:12.040 回答
1

我认为这只是很好的 OO 实践。您可以拥有处理各种迭代器的代码,甚至让您有机会创建自己的数据结构或只是实现迭代器接口的泛型类。您不必担心它背后的实现方式。

于 2008-09-18T04:11:49.993 回答
1

只是 M2C,如果您不知道的话:您可以避免在for-each循环就足够的情况下直接使用迭代器接口。

于 2008-09-18T04:14:15.633 回答
1

最终,因为 Iterator 捕获了适用于大量数据结构的控制抽象。如果您对类别理论 fu 有所了解,那么您可能会对这篇论文大吃一惊:迭代器模式的本质

于 2008-09-18T05:21:39.340 回答
0

好吧,似乎第一个要点允许多线程(或单线程,如果你搞砸了)应用程序不需要锁定集合以防止并发冲突。例如,在 .NET 中,您不能同时枚举和修改集合(或列表或任何 IEnumerable),而无需锁定或继承 IEnumerable 和覆盖方法(我们会遇到异常)。

于 2008-09-18T04:06:59.117 回答
0

迭代器只是添加了一种遍历项目集合的常用方法。一个不错的功能是 i.remove() ,您可以在其中从您正在迭代的列表中删除元素。如果您只是尝试正常从列表中删除项目,它会产生奇怪的效果或抛出和异常。

接口就像实现它的所有事物的合同。您基本上是在说.. 任何实现迭代器的东西都保证具有这些行为相同的方法。如果您只关心代码中处理的所有迭代器类型,您也可以使用它来传递迭代器类型。(您可能不在乎它是什么类型的列表..您只想传递一个迭代器)您可以将所有这些方法独立地放在集合中,但您不能保证它们的行为相同,或者它们甚至具有相同的名称和签名。

于 2008-09-18T04:12:41.690 回答
0

迭代器是 Java 中可用的众多设计模式之一。设计模式可以被认为是方便的构建块、样式、代码/结构的使用。

要阅读有关迭代器设计模式的更多信息,请查看该网站,其中讨论了迭代器以及许多其他设计模式。这是 Iterator 网站上的一个片段:http: //www.patterndepot.com/put/8/Behavioral.html

迭代器是最简单和最常用的设计模式之一。迭代器模式允许您使用标准接口在数据列表或数据集合中移动,而无需了解该数据的内部表示的详细信息。此外,您还可以定义执行某些特殊处理并仅返回数据集合的指定元素的特殊迭代器。

于 2008-09-18T04:23:16.563 回答
0

迭代器可用于任何类型的集合。它们允许您针对项目集合定义算法,而不管底层实现如何。这意味着您可以处理 List、Set、String、File、Array 等。

十年后,您可以将您的 List 实现更改为更好的实现,并且算法仍然可以无缝地运行它。

于 2008-09-18T04:34:31.700 回答
0

当您在 Java 中处理集合时,迭代器很有用。

使用For-Each循环 (Java1.5) 对集合或数组或列表进行迭代。

于 2008-09-18T05:21:04.523 回答
0

java.util.Iterator 接口用于 Java 集合框架中,以允许修改集合,同时仍对其进行迭代。如果您只想干净地迭代整个集合,请改用 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 , 除非您通过 ArrayList 类的 set() add() 或 remove() 方法同时(同时)更改列表。

于 2015-10-21T03:51:19.007 回答