1. 迭代器产生的背景

你已经很熟悉了HashSet。如果你真的研究过它,而不仅仅是阅读一节课,那么你应该问这个问题:

如何在屏幕上显示所有 HashSet 元素的列表?毕竟接口是没有get()set()方法的!

并且HashSet在这个限制中并不孤单。除了HashSet,还有很多其他的集合不允许通过索引检索元素,因为元素没有定义的顺序。

多年来,程序员发明了许多复杂的数据结构,例如图和树。或列表列表。

当添加新元素或删除现有元素时,许多容器会更改其元素的顺序。例如,列表以特定顺序存储元素,当添加新元素时,它几乎总是插入到列表的中间。

我们还会遇到这样的情况,即有一个容器存储元素但没有以任何固定顺序存储。

现在假设我们要将此类集合中的所有元素复制到数组或列表中。我们需要获取所有元素。我们不关心迭代元素的顺序——重要的是不要多次迭代相同的元素。我们该怎么做?


2.集合的迭代器

迭代器被提议作为上述问题的解决方案。

迭代器是与集合关联的特殊对象,它有助于遍历集合中的所有元素而不重复任何元素。

您可以使用以下代码获取任何集合的迭代器:

Iterator<Type> it = name.iterator();

其中name是集合变量的名称,Type是集合元素的类型,iterator()是集合的方法之一,it是迭代器变量的名称。

迭代器对象有 3 个方法:

方法 描述
Type next()
返回集合中的下一个元素
boolean hasNext()
检查是否还有未遍历的元素
void remove()
移除集合的当前元素

这些方法有点类似于 Scanner 类的nextInt)hasNextInt()方法。

next()方法返回我们从中获得迭代器的集合的下一个元素。

hasNext()方法检查集合是否有迭代器尚未返回的其他元素。

以下是显示 a 的所有元素的方法HashSet

代码 笔记
HashSet<String> set = new HashSet<String>();

set.add("Hallo");
set.add("Hello");
set.add("Hola");
set.add("Bonjour");
set.add("Ciao");
set.add("Namaste");

Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
创建一个HashSet存储String元素的对象。


我们将各种语言的问候语添加到set变量中。




获取集合的迭代器对象set
只要还有元素

获取下一个元素
将元素显示在屏幕上


3.For-each循环

迭代器的主要缺点是你的代码变得比使用for循环更麻烦。

for为了比较,让我们使用循环和迭代器显示列表:

迭代器 for循环
ArrayList<String> list = new ArrayList<String>();

Iterator<String> it = list.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}

是的,使用循环遍历 an 的元素要好得多ArrayList——一切都变得更短了。

但是 Java 的创造者再次决定给我们灌些糖。对我们来说幸运的是,它是语法糖

他们为 Java 提供了一种新的循环,并称之为循环for-each。这是它的一般外观:

for(Type name:collection)

其中collection是集合变量的名称,Type是集合中元素的类型,name是在循环的每次迭代中从集合中获取下一个值的变量的名称。

这种循环使用隐式迭代器遍历集合的所有元素。这是它的实际工作方式:

For-each 循环 编译器看到的:使用迭代器循环
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();

while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}

当编译器for-each在您的代码中遇到循环时,它只是将其替换为右侧的代码:它添加一个调用以获取迭代器以及任何其他缺失的方法调用。

程序员喜欢for-each循环,并且在需要遍历集合的所有元素时几乎总是使用它。

ArrayList即使使用循环遍历列表for-each看起来也更短:

For-each 循环 for循环
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}


for-each4. 删除循环中的元素

循环for-each有一个缺点:它不能正确地删除元素。如果你这样写代码,你会得到一个错误。

代码 笔记
ArrayList<String> list = new ArrayList<String>();

list.add("Hallo");
list.add("Hello");
list.add("Hola");
list.add("Bonjour");
list.add("Ciao");
list.add("Namaste");

for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}












删除操作会产生错误!

这是一个非常好的和易于理解的代码,但它不会工作。

重要的!

在使用迭代器遍历集合时,您不能更改它。

有三种方法可以绕过此限制。

1.使用不同类型的循环

When traversing an ArrayList collection,您可以使用带有计数器变量的普通循环i

代码
for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);

   if (str.equals("Hello"))
   {
      list.remove(str);
      i--; // We need to decrease i, because the remove operation shifted the elements
   }
}

但是,此选项不适用于HashSetHashMap集合

2.使用显式迭代器

您可以显式使用迭代器并调用它的remove()方法。

有效的版本 无效的版本
Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   if (str.equals("Hello"))
       it.remove();
}

for (String str: list) { if (str.equals("Hello")) list.remove(str); }

请注意,我们remove()在迭代器对象上调用该方法!迭代器知道该项目已被删除并且可以正确处理这种情况。

3.使用集合的副本

您还可以创建集合的副本,然后在循环中使用副本for-each并从原始集合中删除元素。

代码 笔记
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
创建集合的副本非常简单



循环使用迭代器来创建集合的副本。
元素已从list集合中移除。

集合被复制得相当快,因为​​元素本身没有被复制。相反,新集合存储对旧集合中已存在的元素的引用。