如果我使用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)吗?
答案 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-1)/2
个操作,即O(n ^ 2)。
但是如果你使用了一个内部跟踪下一个元素的迭代器(即前进一步),那么迭代整个列表就是O(n),这是一个很大的改进。
答案 3 :(得分:1)
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),这更快。