38

我正在阅读问题“我们是否需要在 ArrayList 上使用迭代器? ”的答案。

在回答中,用户这样说:“使用 ArrayLists 的迭代器的一个重要用例是当您想要在迭代时删除元素”。

这甚至可以在 Java 中使用 ArrayList 的 remove 方法来实现。我的问题是为什么我们需要 ArrayList 中的迭代器?

考虑代码:

import java.util.*;
public class ocajp66 {
    public static void main(String[] args) {
        ArrayList a = new ArrayList();
        for (int i = 0; i < 10; i++) {
            a.add(i);
        }
        System.out.printf("BEFORE ITERATOR\n");
        for (int i = 0; i < a.size(); i++) {
            System.out.printf("I:%d\n", a.get(i));
        }
        System.out.printf("AFTER ITERATOR\n");
        Iterator i = a.iterator();
        while (i.hasNext()) {
            System.out.printf("I:%d\n", i.next());
        }
    }
}

任何人都可以解释迭代器的重要性吗?如果你能用代码解释我,那就太好了。

4

5 回答 5

58

正如您所说,当您想要在迭代数组内容时删除内容时使用迭代器。如果您不使用迭代器,而只是有一个 for 循环并在其中使用 remove 方法,您将得到异常,因为在您迭代时数组的内容会发生变化。例如:您可能认为在 for 循环开始时数组大小为 10,但一旦删除东西就不会是这种情况。所以当您到达最后一个循环时,可能会有 IndexOutofBoundsException 等。

于 2013-04-14T14:56:18.053 回答
16

很明显,一个ArrayList类似的 API 可以在没有该iterator()方法的情况下工作。但是, anArrayList是 aCollection并且iterator()方法是在Collection接口中定义的......所以ArrayList 必须实现它。

从 an 中删除的要点ArrayList是,通过索引进行操作需要一些思考:

    for (int i = 0; 
         i < a.size(); // Hoist this at your peril
         i++) {
        if (a.get(i) == something) {
            a.remove(i);
            i--;  // Leave this out at your peril
        }
    }

如果您需要在从循环调用的方法中删除列表元素,情况会变得更糟......因为该方法必须说它已经删除了一个元素,以便调用者可以调整循环索引。

iteratoran 上好东西的第三个原因ArrayList是它允许您使用 Java 5 的for (type var : iterable) ...语法。

底线是您不必在实例使用迭代器。ArrayList如果你不想,那就不要。

于 2013-04-14T14:59:29.667 回答
8

这是一个如何通过几种不同方式获得所需结果的示例。这种冗余并不是 Java 独有的。

  • for (int i=0; i < myArray.length; i++) { ... }

这种语法是在 Java 的早期版本中引入的。它在 for { } 循环中迭代一个普通的 Java 数组。这通常是安全的,因为 Java 数组是固定长度的,因此不可能出现“索引越界”异常。

  • for (int i=0; i < myArrayList.size(); i++ { ... }

在引入了引入 ArrayList 的 Collections API 之后,此语法反映了 Java 的更高版本。如上所述,实现 Collection 接口的类必须实现 Iterator,但您不必使用它。这个 for { } 循环没有,但这里的危险是 ArrayLists 不是固定大小的。如果它应该在你的 for 循环体中收缩,就会导致异常。

  • 对于 (MyArrayType t : myArrayList) { }

此语法也在 Java 的后续版本中发布。它被称为增强的 for 循环。任何通过实现 Iterable 接口来提供 Iterator 的集合类都可以利用此语法。这允许迭代集合中的项目,而无需显式实例化迭代器。在 JavaFX 应用程序中使用它的最喜欢的方法是循环通过一堆控件来将属性设置为一个值,例如。重置一组 TextField 的内容:

for (TextField tf : new TextField[] { txtf1, txtf2, txtf3, txtfa, txtfb, txtfc}) {
    tf.setText("");
}
  • 而 (myCollectionIterator.hasNext()) { }

你总是可以显式地实例化一个迭代器。当集合大小发生变化时(来自集合自己的方法),这可以安全使用。说 Iterator 更接近于 Iterable 接口的属性而不是核心 Java 语言的特性是正确的。但是由于后来的 Java 版本,您仍然可以将其用作类似语言的功能(在增强的 for 循环中)。

这些结构提供了冗余,但它们并不相同。每一个都有细微差别,使一个在给定时间特别有用。您应该使用所有这些。

于 2013-04-14T15:27:28.433 回答
1

问:为什么我们需要 ArrayList 中的迭代器?

我们不这样做 - 正如您在代码中所示,您可以在没有迭代器的情况下迭代并在 ArrayList 上执行核心操作。但是有功能很好。

问:谁能解释一下迭代器的意义?

除了它的设计价值之外,我还可以看到它的快速故障功能。我从ArrayList 文档中引用了这一段:

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在创建迭代器后的任何时间对列表进行结构修改,除了通过迭代器自己的 remove 或 add 方法之外,迭代器将抛出 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

您正在寻找代码,您实际上可以在这里看到 ArrayList 的迭代器实现:ArrayList.java

于 2013-04-14T15:17:21.377 回答
0

对于您的问题,如果我们使用list.remove()方法而不是iterator.remove()thenIndexOutOfBoundsException将被抛出。

list.remove()如果您在找到要删除的特定对象/索引后放置语句,则可以安全使用,break以便它将退出循环而没有任何异常(如IndexOutOfBoundsException

如果我们在同步环境中使用迭代器,以下迭代器代码仍然可以抛出。 ConcurrentModificationException

List<String> empNames = new ArrayList<String>();
        synchronized (empNames) {
            Iterator<String> iterator = empNames.iterator();
            while (iterator.hasNext()) {
                iterator.next();
                empNames.add("Another Name"); // throws
                // ConcurrentModificationException
            }
        }
于 2013-04-14T15:35:17.707 回答