我有一个包含一个元素的列表,但以后可能会有更多元素。我使用的列表是一个 ArrayList。我发现通过调用 List.get(0) 而不是使用 for 循环,我得到了更快的结果。这是为什么呢?循环 1 个项目不应该和获取 1 个项目相同吗?如果不是,那么我如何获得类似的性能?我知道我的数组最终会大于一个项目。我在 opengl 渲染器的主体中使用此循环。当我使用循环时,我的 fps 下降了 45。
编辑:我已经解决了这个问题。我的渲染器每次渲染时都会向列表中添加一个新值。
在 Java 中使用增强的 for 循环(for-each)会生成如下所示的编译代码:
Iterator<Thing> it = list.iterator();
while(it.hasNext()) {
yourLoop.loopBody(it.next());
}
您可能期望它等同于此?
for (int i = 0; i < list.size(); i++) {
yourLoop.loopBody(list.get(i));
}
但事实并非如此,构造迭代器需要您观察的额外时间。
不知道List
实现,就无法真正给出具体的答案。例如,ArrayList
由数组支持,因此调用get
本质上是一种数组访问。
另一方面,使用 for
循环的"foreach"版本需要创建一个Iterator
。这可能是减速的原因。某些实现具有Iterator
或ListIterator
的复杂实现。
那么答案就是风格和可读性的问题。大多数Java程序员会说foreach循环更具可读性,并且更清晰。并且通过一些List
实现(例如 LinkedList
)快得多。
我不是集合方面的专家,但设置迭代必须比简单地获取特定项目更昂贵。
通过设置 foreach,我们不知道集合中有多少项,因此无论列表有多少项,它都会将其设置为迭代。
一般来说,如果你担心速度,如果你知道你正在使用ArrayList,你最快的方法是:
int size = list.size();
for (int i=0;i<size;i++) {
Thing thing = list.get(i);
}
虽然"size"方法的开销很小,但它仍然是不需要在每个循环中调用的开销。 ArrayList 上的 get() 方法是 O(1)。 这应该为迭代器提供类似的性能。 如果您查看 ArrayList 的迭代器代码,它非常简单,唯一真正的额外开销来自对象创建和并发修改检查。
然而,对于 LinkedList,这种类型的循环会给出可怕的性能,因为 LinkedList 上的 .get() 方法是 O(n),而迭代器会快得多,因为它只是对每次迭代进行指针检查和重新分配,这使得每次调用 next() 时都是 O(1)。