“嗨,阿米戈!”

“嗨,艾莉!”

“今天我想给大家讲讲迭代器。”

“迭代器实际上是与集合同时发明的。集合的主要目的是存储元素,而迭代器的主要目的是一个一个地检索这些元素。”

“搞到一套元素有什么难的?”

“首先,一些集合中的元素,例如 Set,没有既定的顺序和/或顺序不断变化。”

“其次,一些数据结构可能以非常复杂的方式存储对象:在不同的组、列表等中。换句话说,按顺序分发所有元素将是一项非常重要的任务。”

“第三,集合往往会发生变化。假设您决定显示集合的全部内容,但就在输出中间,JVM 切换到另一个线程,该线程替换了集合的一半元素。因此,您得到的不是输出,而是谁知道呢。”

“唔...”

“但是!这些正是迭代器可以解决的问题。迭代器是集合中的一个特殊对象,一方面可以访问其所有私有数据并知道其内部结构,另一方面,实现了公共 Iterator 接口,让每个人都知道如何使用它。

“一些迭代器有一个内部数组,当创建迭代器时,集合中的所有元素都会被复制到这个数组中。这确保了对集合的任何后续更改都不会影响元素的数量或顺序。”

“我认为你在使用for each时遇到过这个问题。你不能同时遍历一个集合并从中删除元素。这完全是因为迭代器的工作方式。”

“在添加到并发库的新集合中,迭代器被重新设计以消除这个问题。”

“让我提醒你迭代器是如何工作的。”

“Java 有一个特殊的 Iterator 接口。这是它的方法:”

Iterator<E> 接口的方法 描述
boolean hasNext() 检查是否还有更多元素
E next() 返回当前元素并移动到下一个。
void remove() 移除当前元素

“迭代器可让您连续获取集合的所有元素。将迭代器视为类似于 InputStream 的东西更合乎逻辑——它拥有所有数据,但其任务是按顺序输出数据。”

“   next () 方法返回集合中的下一个元素。”

hasNext () 方法用于检查是否还有更多元素。”

“而remove () 移除当前元素。”

“任何问题?”

“为什么这些方法有这么奇怪的名字?为什么不是 isEmpty() 和 getNextElement()?”

“那不是更有意义吗?”

“这会更有意义,但名称来自 C++ 语言,其中迭代器出现得更早。”

“原来如此,我们继续吧。”

“除了迭代器,还有一个Iterable接口,所有支持迭代器的集合都必须实现这个接口,它只有一个方法:”

Iterable<T> 接口的方法 描述
Iterator<T>iterator() 返回一个迭代器对象

“您可以在任何集合上使用此方法来获取迭代器对象以遍历其元素。让我们遍历 TreeSet 中的所有元素

例子
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}

“像这样使用迭代器不是很方便——有太多多余和明显的代码。当for-each循环出现在 Java 中时,情况变得更简单了。”

“现在这段代码更加紧凑和可读:”

TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}
TreeSet<String> set = new TreeSet<String>();

for(String item : set)
{
 System.out.println(item);
}

“这是相同的代码!两种情况下都使用了迭代器。”

“只是它的使用隐藏在for-each循环中,注意右边的代码根本没有红字,完全隐藏了迭代器的使用。”

for-each循环可用于支持迭代器的任何对象。换句话说,您可以编写自己的类,向其添加 iterator ( ) 方法,并在for-each构造中使用其对象。”

“哇!当然,我并不急于编写自己的集合和迭代器,但前景仍然很诱人。我会记下来的。”

此外,还有另一种流行的迭代器类型,它甚至有自己的接口。我说的是列表迭代器,即ListIterator

“不管它们的实现如何,列表都会保持元素的顺序,这使得通过迭代器使用它们更方便一些。”

“这是ListIterator <E> 接口的方法:”

方法 描述
boolean hasNext() 检查前面是否还有更多元素。
E next() 返回下一个元素。
int nextIndex() 返回下一个元素的索引
void set(E e) 改变当前元素的值
boolean hasPrevious() 检查后面是否有任何元素。
E previous() 返回前一个元素
int previousIndex() 返回前一个元素的索引
void remove() 移除当前元素
void add(E e) 将一个元素添加到列表的末尾。

“换句话说,在这里我们可以向前和向后移动。还有一些其他的小特征。”

“嗯,那是个有趣的东西。它用在什么地方?”

“假设你想在链表上来回移动。get 操作会相当慢,但 next() 操作会非常快。”

“嗯,你说服了我,我会记住的。”

“谢谢,艾莉!”