【问题标题】:Why ArrayList.addAll(...) doesn't check the given colleciton for nonempty?为什么 ArrayList.addAll(...) 不检查给定的集合是否为非空?
【发布时间】:2020-10-03 18:35:00
【问题描述】:

我们发现 ArrayList.addAll(...) 方法不检查给定集合是否为空。 这意味着我们将重新分配内存(将调用 System.arrayCopy(...)),即使我们实际上并不需要它。

我们是否应该为优化添加 IF 检查?

例如:第一个代码的运行时间比第二个代码快 8 倍以上。 看起来我们的优化工作正常,为什么还没有实现呢?

List<Integer> existed = new ArrayList<>();
List<Integer> empty = new ArrayList<>();
for (int i = 0; i < 100_000_000; i++) {
    if(!empty.isEmpty())
       existed.addAll(empty);
}

VS

List<Integer> existed  = new ArrayList<>();
List<Integer> empty = new ArrayList<>();
for (int i = 0; i < 100_000_000; i++) {
    existed.addAll(empty);
}

【问题讨论】:

  • “重新分配内存(将调用 System.arrayCopy(...))”是什么意思? System.arraycopy(…) 不分配内存。推荐阅读:How do I write a correct micro-benchmark in Java?
  • 另外请说明 JDK 版本以及您的测量方式
  • @fps 我们使用 Java 8

标签: java optimization arraylist collections java-8


【解决方案1】:

在 JDK14 中,ArrayList.addAll() 复制集合以添加为数组并增加 ArrayList 修改计数 - 即使没有要添加的元素。

public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    modCount++;
    int numNew = a.length;
    if (numNew == 0)
        return false;
    ...

因此,您的测试用例事先没有使用 isEmpty(),这将导致不必要地分配 100,000,000 个新 Object[0] 实例,并将修改计数增加相同的次数,这可以解释为什么它看起来更慢。

请注意,除非数组的增长超出现有数组的容量,否则不会调用 System.arrayCopy。

我刚刚扫描了自己的代码,我怀疑添加 isEmpty() 测试是否会加快代码速度,因为通常每次都会添加一些内容,因此不需要进行这些额外的检查。您需要根据自己的情况做出决定。

【讨论】:

    猜你喜欢
    • 2011-03-08
    • 2011-04-24
    • 2016-06-20
    • 2017-05-28
    • 2013-06-19
    • 2017-05-07
    • 2018-08-09
    • 2021-11-02
    • 1970-01-01
    相关资源
    最近更新 更多