Java 为什么对列表进行迭代要比对其进行索引更快?

Java 为什么对列表进行迭代要比对其进行索引更快?,java,list,iterator,Java,List,Iterator,阅读报告,上面写着: 列表接口提供了四种对列表元素进行位置(索引)访问的方法。列表(如Java数组)是基于零的。请注意,对于某些实现(例如LinkedList类),这些操作的执行时间可能与索引值成比例。因此,如果调用方不知道实现,那么在列表中的元素上进行迭代通常比通过列表进行索引更可取 这到底意味着什么?我不明白得出的结论。这里暗示了答案: 请注意,对于某些实现(例如LinkedList类),这些操作的执行时间可能与索引值成比例 链表没有固有的索引;调用.get(x)将要求列表实现找到第一个条目

阅读报告,上面写着:

列表接口提供了四种对列表元素进行位置(索引)访问的方法。列表(如Java数组)是基于零的。请注意,对于某些实现(例如LinkedList类),这些操作的执行时间可能与索引值成比例。因此,如果调用方不知道实现,那么在列表中的元素上进行迭代通常比通过列表进行索引更可取


这到底意味着什么?我不明白得出的结论。

这里暗示了答案:

请注意,对于某些实现(例如LinkedList类),这些操作的执行时间可能与索引值成比例

链表没有固有的索引;调用
.get(x)
将要求列表实现找到第一个条目,并调用
.next()
x-1次(对于O(n)或线性时间访问),其中一个数组支持的列表可以在O(1)或恒定时间内索引到
backingarray[x]

如果你看一下,你会看到评论

所有操作的执行都与双链接列表的预期一样。索引到列表中的操作将从开始或结束遍历列表,以更接近指定索引的为准

鉴于有相应的

列表接口的可调整大小的数组实现。实现所有可选的列表操作,并允许所有元素,包括null。除了实现列表接口外,此类还提供了一些方法来操作内部用于存储列表的数组的大小。(该类大致相当于Vector,只是不同步。)

大小
获取
设置
迭代器
列表迭代器
操作以恒定时间运行。添加操作在摊销的固定时间内运行,即添加n个元素需要O(n)个时间。所有其他操作都在线性时间内运行(粗略地说)。与
LinkedList
实现相比,常数因子较低


A有一个指向此资源的答案,您可能会发现它很有用。

要查找
链接列表的第i个元素,实现将遍历所有元素,直到第i个元素

所以

for(int i=0;i
在链表中,每个元素都有一个指向下一个元素的指针:

head -> item1 -> item2 -> item3 -> etc.
要访问
item3
,您可以清楚地看到,您需要从头部穿过每个节点,直到到达item3,因为您无法直接跳转

因此,如果我想打印每个元素的值,如果我这样写:

for(int i = 0; i < 4; i++) {
    System.out.println(list.get(i));
}
这是非常低效的,因为每次索引时,它都会从列表的开头重新启动并遍历每一项。这意味着您的复杂性实际上是
O(N^2)
只是为了遍历列表

如果我这样做:

for(String s: list) {
    System.out.println(s);
}
那么会发生这样的情况:

head -> print head
head -> item1 -> print item1
head -> item1 -> item2 -> print item2
head -> item1 -> item2 -> item3 print item3
head -> print head -> item1 -> print item1 -> item2 -> print item2 etc.
全部在单个遍历中,即
O(N)


现在,转到
List
的另一个实现,即
ArrayList
,它由一个简单的数组支持。在这种情况下,上述两种遍历都是等效的,因为数组是连续的,所以它允许随机跳转到任意位置。

虽然接受的答案肯定是正确的,但我想指出一个小缺陷。引用都铎的话:

现在,转到列表的另一个实现,即ArrayList, 这一个由一个简单的数组支持在这种情况下,上述两种 遍历是等效的,因为数组是连续的,所以它允许 随机跳转到任意位置

这并不完全正确。事实是

使用ArrayList,手写计数循环的速度大约快3倍

请注意,手写循环指的是索引迭代。我怀疑这是因为迭代器与增强的for循环一起使用。在由连续数组支持的结构中,它会产生较小的性能损失。我还怀疑这对于Vector类可能是正确的

我的规则是,尽可能使用增强的for循环,如果您真的关心性能,请仅对ArrayLists或Vectors使用索引迭代。在大多数情况下,您甚至可以忽略这一点-编译器可能会在后台对此进行优化


我只想指出,在Android的开发环境中,ArrayList的两种遍历都不一定是等价的。值得思考。

迭代带有偏移量的列表进行查找,例如
i
,类似于画家Shlemiel的算法

Shlemiel得到了一份街道油漆工的工作,画虚线 沿着路中间走。第一天他拿了一罐油漆 走到马路上,走完300码的路。“真漂亮 “很好!”他的老板说,“你是个速度快的工人!”并给了他一个kopeck

第二天,施莱米尔只完成了150码。“嗯,那不是 几乎和昨天一样好,但你还是个速度快的工人。150码 “很体面,”他给了他一个科比

第二天,Shlemiel在30码外的道路上刷油漆。“只有30个!”他喊道 他的老板。“那太不可接受了!第一天你做了十次 这么多活儿!怎么回事?”

“我没办法,”施莱米尔说。“我每天都越来越远 远离油漆罐!”


这个小故事可以让我们更容易理解内部发生了什么以及为什么效率如此低下。

小提示:如果索引位于列表的后半部分,LinkedList将从列表的末尾进行搜索,但这并不能真正改变根本的效率低下
head -> print head -> item1 -> print item1 -> item2 -> print item2 etc.