java - 在 for 循环比较中使用集合大小

标签 java collections

Java 中Collections 的size() 方法是否有编译器优化?

考虑以下代码:

for(int i=0;i<list.size();i++)
      ...some operation.....

每个 i 都会调用 size() 方法。找出大小并重复使用它不是更好吗? (方法调用有开销)。

final int len = list.size()
for(int i=0;i<len;i++)
      ...some operation.....

然而,当我对这两个代码片段进行计时时,没有明显的时间差异,即使 i 高达 10000000。 我在这里遗漏了什么吗?

更新 1: 据我所知,除非集合发生变化,否则不会再次计算大小。但是必须有一些与方法调用相关的开销。编译器是否总是内联这些(参见 Esko 的回答)?

更新 2: 我的好奇心被进一步点燃了。从给出的答案中,我看到好的 JIT 编译器通常会内联这个函数调用。但是他们仍然必须确定集合是否被修改过。我不接受一个答案,希望有人能给我一些关于编译器如何处理的指示。

最佳答案

好的,这里是 JDK 源代码的摘录(JDK 文件夹中的 src.zip):

public int size() {
    return size;
}

这是来自 ArrayList,但我认为其他集合也有类似的实现。现在,如果我们想象编译器内联 size() 调用(这非常有意义),您的循环将变成:

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

(好吧,让我们忘记大小是私有(private)的。)编译器如何检查集合是否被修改?它不需要也不需要这样做的答案是因为字段中的大小已经可用,所以它所要做的就是在每次迭代时访问大小字段,但是访问一个 int 变量是非常快的手术。请注意,它可能只计算一次地址,因此它甚至不必在每次迭代时取消引用列表。

当集合被修改时会发生什么,比如说,通过 add() 方法?

public boolean add(E e) {
    ensureCapacity(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

如您所见,它只是增加了尺寸字段。所以编译器实际上不需要做任何事情来确保它可以访问最新的大小。唯一的异常(exception)是,如果您从另一个线程修改集合,则需要同步,否则循环线程可能会看到其本地缓存的大小值,该值可能会更新也可能不会更新。

关于java - 在 for 循环比较中使用集合大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4438710/

相关文章:

java - 添加 View 数组会使 Android 应用程序崩溃

java - 如何在@Backoff中的delayExpression中进行算术运算(Spring-retry)

java - 如何使用嵌入了WildFly 8.0.0的Arquillian?

java - 如何获得hashmap中的两个最大值

java - GWT 与 UIBinder - 如何以编程方式添加 ScrollPanel?

java - 带有 OnTouchListener 的按钮,在 Android Studio 中按住时持续执行某些操作

java - 为什么将第一项添加到集合中比第二项慢得多?

java - Collections.unmodifiableList 是否存在性能风险?

一组 (x,y) 点的 C# 数据结构

Java -> Scala,集合性能