【问题标题】:Java : Is the get method of an Arraylist cached?Java:Arraylist 的 get 方法是否被缓存?
【发布时间】:2017-10-18 10:56:09
【问题描述】:

Arraylist 对象是否将上次请求的值存储在内存中以便下次更快地访问它?还是我需要自己做?

或者更具体地说,就性能而言,这样做是否更好:

for (int i = 0; i < myArray.size(); i++){
    int value = myArray.get(i);

    int result = value + 2 * value - 5 / value;
}

而不是这样做:

for (int i = 0; i < myArray.size(); i++)
    int result = myArray.get(i) + 2 * myArray.get(i) - 5 / myArray.get(i);

【问题讨论】:

  • 性能方面,一点都不重要。
  • @Kayaman 即使使用大型 ArrayList?
  • 这和你在第一个变量中声明一个额外的变量是一样的。但它更清晰
  • 考虑到ArrayList 不是线程安全的集合并且循环很紧,无论如何它都会在处理器缓存中。
  • 考虑使用增强的 for 循环(虽然我不确定您的原始代码是否有意义,因为它总是在不使用值的情况下重新分配“结果”): for (int value : myArray) { int result = 价值 + 2 * 价值 - 5 / 价值;在 Java8 中,您可以使用 lambda 使其更加简洁。

标签: java performance memory arraylist get


【解决方案1】:

就性能而言,一点都不重要。不,ArrayList 不缓存任何内容,尽管 JITted 最终结果可能是一个不同的问题。

如果您想知道要使用哪个版本,请使用第一个。更清楚了。

【讨论】:

  • "一点都不重要。"不完全正确,第二个选项将使用三个调用和三次自动拆箱,这比访问变量更昂贵。我怀疑你是否注意到它..
  • 没关系,因为它是一个无用的微优化。这也无关紧要,因为 JIT 很可能会将其转化为比问题中看起来更有效的东西。
  • 您不知道 JIT 何时启动,JIT 是否知道它可能会将对 arraylist 的三个调用优化为一个? JIT 如何知道哪些调用保证在连续调用中返回相同的对象
  • 这超出了这个问题的范围。让我们说,JIT 比许多提问的人聪明得多。
  • “你不知道 JIT 什么时候开始”,我不需要。虽然如果我有兴趣,我可以调整它。 “JIT 会知道它可能会将对 arraylist 的三个调用优化为一个吗?”,它可能会做很多更复杂的事情,所以这几乎没有什么特别的。 “JIT 如何知道哪些调用可以保证在连续调用中返回相同的对象?”,它当然会分析正在运行的代码。它还能如何有效地优化?您给予 JIT 的功劳远低于应得的(而手动微优化远远超过应得的)。
【解决方案2】:

您可以通过查看实际来源自己回答您的(第一个)问题:

public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

所以:不,没有进行缓存,但您也可以看到在性能方面没有太大影响,因为get 方法本质上只是对数组的访问。

但出于某些原因,避免多次调用仍然是件好事:

  • int result = value + 2 * value - 5 / value 更容易理解(即意识到您在计算中使用了 3 次相同的值)
  • 如果您稍后决定更改基础列表(例如更改为 LinkedList),最终可能会对性能产生影响,然后必须更改您的代码才能绕过它。
  • 只要不同步对列表的访问,如果在两次调用之间发生了对 set(index, value) 的调用(即使在像这样的小源块中,重复调用 get(index) 可能实际上返回不同的值,有可能发生 - BTST)

第二点在如何访问列表的所有值方面也有影响,如果您要遍历列表中的所有元素,则导致决定完全避免list.get(i)。在这种情况下,最好使用Iterator 或流:

您的代码将如下所示:

Iterator it = myArray.iterator();
while (it.hasNext()) {
    int value = it.next();

    int result = value + 2 * value - 5 / value;
}

LinkedList 在尝试通过特定索引访问其中的元素时非常慢,但可以从一个元素快速迭代到下一个元素,因此 LinkedList 返回的 Iterator 使用了这一点,而 Iterator ArrayList 返回的只是访问内部数组(无需执行重复的范围检查调用,您可以在上面的get-方法中看到

【讨论】:

  • "只要不同步对列表的访问,如果两次调用之间调用了 set(index, value),重复调用 get(index) 实际上可能返回不同的值地方”。不,如果您不同步访问,很可能不会返回不同的值,即使有另一个线程修改了列表。
  • 我并没有说它可能会发生,只有它可以发生。 BTST 是“been there, seen that”的缩写,即我在现实生活中确实目睹了这种事情。这是非常罕见的,但如果它真的发生了,它仍然是可能的,而且非常讨厌。
  • 描述有点不清楚。如果你在线程之间共享一个可变对象,你肯定需要处理同步。只是如果您不这样做,那么您很可能看不到任何差异,因为使用了缓存值。我不太确定你想在那里表达什么。更不用说与并发相关的问题也超出了这个问题。
  • 描述基于我们谈论List&lt;Integer&gt;的问题。否则你是对的,当然,谢谢你的澄清。关于“超越问题”:问题是“什么更好”,范围很广。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-24
相关资源
最近更新 更多