为什么我们需要在Java中的ArrayList上使用iterator?

时间:2013-04-14 14:44:32

标签: java arraylist iterator

我正在阅读问题中提到的答案 “Do we ever need to use Iterators on 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());
        }
    }
}

有人可以解释迭代器的重要性吗?如果你能用代码向我解释,那将是非常好的。

5 个答案:

答案 0 :(得分:50)

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

答案 1 :(得分:14)

很明显,类似ArrayList的API可以在没有iterator()方法的情况下运行。但是,ArrayListCollection,而iterator()方法是在Collection界面中定义的...所以ArrayList 实施它。

关于从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
        }
    }

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

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

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

答案 2 :(得分:6)

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

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

这种语法是在早期版本的Java中引入的。它迭代for {}循环中的常规Java数组。这通常是安全的,因为Java数组是固定长度的,因此&#34; Index Out of Bounds&#34;例外是不可能的。

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

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

  • for(MyArrayType t:myArrayList){}

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

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

您始终可以显式实例化Iterator。当集合大小发生变化时(从Collection自己的方法),这是安全的。可以肯定地说,Iterator是Iterable接口的一个属性,而不是核心Java语言的一个特性。但是,由于以后的Java版本,您仍然可以将它用作类似语言的功能(在增强的for循环中)。

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

答案 3 :(得分:1)

问:为什么我们需要在ArrayList中使用迭代器?

我们没有 - 正如您在代码中展示的那样,您可以在没有迭代器的情况下迭代并对ArrayList执行核心操作。但是有一个功能很好。

问:有人可以解释迭代器的重要性吗?

除了它的设计价值之外,我能看到的是它的快速失效功能。我引用ArrayList documentation

中的这一段
  

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

您正在寻找代码,您实际上可以在此处查看ArrayList的迭代器实现:ArrayList.java

答案 4 :(得分:0)

对于您的问题,如果我们使用list.remove()方法代替iterator.remove(),则会抛出IndexOutOfBoundsException

如果您在找到要删除的特定对象/索引后放置list.remove()语句,那么

break是安全的,这样它就会从循环中退出而没有任何异常(如IndexOutOfBoundsException

如果我们在同步环境中使用迭代器EVEN,迭代器代码仍然可以抛出 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
            }
        }