在java中寻求对迭代器的进一步理解

时间:2011-08-17 05:20:03

标签: java algorithm iterator performance

如果我使用for循环(循环标准,而不是增强的for语句),我无法看到迭代器在搜索集合时如何提高效率。如果我有如下声明:

(假设 aList 是通用对象的列表,输入E,nextElement引用列表中的下一个元素)

for (int index = 0; index < aList.size(); index++){
E nextElement = aList.get(index);
// do something with nextElement...
}

我的get方法看起来像:

Node<E> nodeRef = head;
for (int i = 0; i < index; i++){
    nodeRef = nodeRef.next;
    // possible other code
}

这基本上是在列表中搜索,一次一个元素。但是,如果我使用迭代器,它不会执行相同的操作吗?我知道迭代器应该是O(1)速度,但如果必须搜索整个列表,它不会是O(n)吗?

6 个答案:

答案 0 :(得分:12)

主要不是效率,IMO。这是关于抽象。使用索引将您绑定到可以有效地检索给定索引的项目的集合(因此它不适用于链接列表,比如说)...并且它不表达您的内容'试图做,这是在列表上迭代。

使用迭代器,您可以表达迭代一系列项目的想法,无论该序列是否可以轻松地被索引,是否提前知道大小,甚至在它实际上是无限的情况下。

你的第二个案例仍然使用for循环来增加一个索引,这不是考虑它的惯用方式 - 它应该只是测试它是否是走到了尽头。例如,它可能是:

for (Node<E> nodeRef = head; nodeRef != null; nodeRef = nodeRef.next)
{
}

现在我们有了正确的抽象:循环表示我们开始的地方(头部),当我们停止时(没有更多的元素)以及我们如何从一个元素转到下一个元素(使用next领域)。这表达了比“我有一个从0开始的计数器更有效地迭代的想法,并且我将在每次迭代时询问特定计数器的值,直到计数器的值大于发生的某个值。是列表的长度。“

我们用 来表达事物的后一种方式,但它并没有像迭代器方法那样真正地说出我们的意思。

答案 1 :(得分:2)

迭代器不是提高效率,而是关于面向对象意义上的抽象。在实现方面,迭代器正在执行与您正在执行的操作类似的操作,一次通过集合一个元素,至少在集合是基于索引的情况下。在检索 next 元素时,它应该是O(1),而不是整个列表。迭代器可以帮助掩盖下面的集合,它可以是链接列表或集合等,但您不必知道。

另外,请注意你的for循环与你想要对每个元素做的特定逻辑的连接,而使用迭代器,你可以从你想要做的任何动作中抽象出循环逻辑。

答案 2 :(得分:1)

我认为你问的问题是指迭代器与使用集合对象上的显式get的for循环的效率。

如果你用get的天真版本编写代码,然后使用它迭代你的列表,那么它会带你

  • “获取”第一个元素的一步
  • “获得”第二个
  • 的两个步骤
  • 获得第三个
  • 的三个步骤
  • ...
  • n步骤以获取最后一个

总计n(n-1)/2个操作,即O(n ^ 2)。

但是如果你使用了一个内部跟踪下一个元素的迭代器(即前进一步),那么迭代整个列表就是O(n),这是一个很大的改进。

答案 3 :(得分:1)

像Jon说的那样,迭代器与效率无关,它们只是抽象出能够迭代集合的概念。所以你是对的,如果你只是在列表中搜索,那么迭代器对于for循环没有任何实际好处,但在某些情况下,迭代器提供了一些简单的for循环难以处理的方法。例如:

while(itr.hasNext()) {
    if(itr.next().equals(somethingBad);
         itr.remove();
}

在其他情况下,迭代器提供了一种遍历集合元素的方法,您无法通过索引获取(例如,哈希集)。在这种情况下,for循环不是一种选择。

答案 4 :(得分:1)

请记住,它也是一种设计模式。

  

“迭代器模式允许遍历聚合的元素而不暴露底层实现。它还将遍历任务放在迭代器对象上,而不是聚合上,这简化了聚合接口和实现,并放置了责任它应该在哪里。“ (来自:Head First Design Pattern)

这是关于封装以及“单一责任”原则。

干杯, 维姆

答案 5 :(得分:0)

您在此处使用链接列表。在没有迭代器的情况下迭代该列表需要O(n ^ 2)步,其中n是列表的大小。 O(n)用于迭代列表,O(n)每次用于查找下一个元素。

另一方面,迭代器会记住它上次访问过的节点,因此只需要O(1)来查找下一个元素。所以最终复杂性是O(n),这更快。