4

我知道这个问题可能很模糊。但我问这个的原因是因为这门课一定是经过深思熟虑的。

在浏览 SO 上的几个问题时,我想到了这个问题。

考虑以下代码:

class A
{

    private int myVar;

    A(int varAsArg)
    {
          myVar = varAsArg;
    }

   public static void main(String args[])
   {

      List<A> myList = new LinkedList<A>();
      myList.add(new A(1));
      myList.add(new A(2));
      myList.add(new A(3));

      //I can iterate manually like this:
      for(A obj : myList)
                 System.out.println(obj.myVar);

      //Or I can use an Iterator as well:
      for(Iterator<A> i = myList.iterator(); i.hasNext();)
      {
         A obj = i.next();
         System.out.println(obj.myVar);
      }
   }
}

从上面的代码中可以看出,我可以替代使用for循环进行迭代,而我可以使用IteratorhasNext()next()方法来做同样的事情。类似地,该方法可以有一个示例remove()。并且有经验的用户评论了使用Iterator类而不是使用for循环遍历List. 为什么?

更让我困惑的是,这个Iterator类只有三个方法。这些功能也可以通过编写一些不同的代码来实现。

有些人可能会争辩说,许多类的功能可以通过编写自己的代码来实现,而不是使用classmake 来实现。是的,没错。但正如我所说,Iterator类只有三个方法。那么,当同样的工作可以用一个简单的代码块完成时,为什么还要麻烦创建一个额外的类,这也不是太复杂而难以理解。


编辑:

虽然我在这里,但由于许多答案都说我无法在不使用的情况下实现删除功能Iterator,所以我只想知道以下内容是否错误,或者是否会产生一些不良结果。

for(A obj : myList)
{
           if(obj.myVar == 1)
                 myList.remove(obj);
}

上面的代码片段不是做同样的事情remove()吗?

4

7 回答 7

7

Iteratorfor早在您在 Java 的演变中展示的声明之前就出现了。这就是它存在的原因。此外,如果您想删除某些内容,使用Iterator.remove()是您可以做到的唯一方法(您不能使用该for语句)。

于 2012-05-25T07:35:54.950 回答
3

首先,for-each 构造实际上使用了幕后的Iterator接口。但是,它不会将底层Iterator实例暴露给用户代码,因此您不能在其上调用方法。

这意味着有些事情需要显式使用Iterator接口,而无法通过使用 for-each 循环来实现。

删除当前元素就是这样一种用例。

有关其他想法,请参阅ListIterator界面。它是一个双向迭代器,支持插入元素和改变光标下的元素。这些都不能通过 for-each 循环来完成。

for(A obj : myList)
{
           if(obj.myVar == 1)
                 myList.remove(obj);
}

上面的代码片段不是和 remove() 做同样的事情吗?

不,不是的。ConcurrentModificationException当您尝试执行此操作时,我所知道的所有标准容器都会抛出。即使它被允许工作,它也是模棱两可的(如果obj两次出现在列表中怎么办?)并且效率低下(对于链表,它需要线性而不是恒定时间)。

于 2012-05-25T07:37:41.247 回答
3

foreach 构造 ( for (X x: list)) 实际上Iterator在内部用作其实现。您可以将其Iterable作为元素来源提供任何内容。

而且,正如其他人已经指出的那样:Java 中的迭代器比 foreach 更长,它提供remove().

另外:您将如何实现自己的提供程序类(myList在您的示例中)?您制作它Iterable并实现一个创建Iterator.

于 2012-05-25T07:40:29.560 回答
2

一方面,Iterator它是在将foreach循环(如上面的代码示例所示)引入 Java 之前创建的。(前者出现在 Java2 中,后者仅出现在 Java5 中)。

从 Java5 开始,foreach 循环确实是最常见场景的首选习惯用法(当您一次迭代一个Iterable,以默认顺序,并且不需要删除或索引元素时)。请注意,foreach 在后台使用标准集合类的迭代器;换句话说,它只是语法糖。

于 2012-05-25T07:36:25.087 回答
1

Iterator,listIterator 都用于允许用户不同的权限,如 list iterator 有 9 个方法,但 iterator 只有 3 个方法,但具有使用 for 循环无法实现的删除功能。枚举是另一件事,它也用于仅提供读取权限。

于 2012-05-25T07:37:49.517 回答
0

迭代器是经典 GoF 设计模式的实现。通过这种方式,您可以从迭代的“技术代码”(迭代器)和您的业务代码中实现清晰的行为分离。

想象一下,您必须更改“下一个”行为(例如,不是通过获取下一个元素,而是获取下一个 EVEN 元素)。如果你只依赖for循环,你将不得不手动更改每个 for 循环,就像这样

for (int i; i < list.size(); i = i+2)

而如果您使用迭代器,您可以简单地覆盖/重写“next()”和“hasNext()”方法,并且更改将在您的应用程序中随处可见。

于 2012-05-25T07:42:49.767 回答
0

我认为你的问题的答案是抽象。编写迭代器是因为抽象迭代不同的集合集。

每个集合都有不同的方法来迭代它们的元素。ArrayList 具有索引访问。队列有 poll 和 peek 方法。Stack 有 pop 和 peek。

通常你只需要迭代元素,这样 Iterator 就会发挥作用。您不关心需要迭代哪种类型的 Collection。您只需调用 iterator() 方法和用户迭代器对象本身来执行此操作。

如果您问为什么不在 Collection 接口上放置相同的方法并摆脱额外的对象创建。您需要知道您在集合中的当前位置,因此您不能在 Collection 中实现 next 方法,因为您不能在不同的位置使用它,因为每次调用 next() 方法时它都会增加索引(简化每个集合都有不同的实现)所以您如果您在不同的地方使用相同的集合,将跳过一些对象。此外,如果集合支持并发,则不能在集合中编写多线程安全的 next() 方法。

通过迭代器以外的其他方式从集合中删除对象通常是不安全的。Iterator.remove() 方法是最安全的方法。对于 ArrayList 示例:for(int i=0;i

于 2012-05-25T08:10:47.443 回答