为什么Collections.addAll()比arrays.addAll()性能好?

Java基础

浏览数:206

2020-6-15

1. Collections.addAll()比arrays.addAll()更快吗?

在《Java核心编程》这本书中,“持有对象”这一章有个地方讲到:

Collections.addAll()比arrays.addAll()方法快得多!

我们知道,Collections是一个工具类,它提供了一组操作集合类的静态方法,包括排序、交换元素、拷贝、最大值、最小值等,使用起来非常方便。addAll()方法就是其中之一,它用来将一组元素添到一个集合中:

 List<String> arrays = new ArrayList<>();
 Collections.addAll(arrays, "hello",  "world");

arrays.addAll()方法,是List对象的一个方法,它实现的功能与Collections.addAll()类似,也是将一组元素添加到数组中:

List<String> arrays = new ArrayList<>();
arrays.addAll(Arrays.asList("hello",  "world"));

2. 是不是真的更快?

尽信书不如无书,书上虽然是这样说的,那是不是真的Collections.addAll()arrays.addAll()性能好很多呢?我们不妨来测试一下:

public class Test {
    public static void main(String[] args) {

        //循环次数
        int count = 100000000;
        System.out.println("Collections.addAll()耗时:" + collectionsTest(count) + "ms");
        System.out.println("arrays.addAll()耗时:" + arraysTest(count) + "ms");
    }

    public static long collectionsTest(int count) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            List<String> arrays = new ArrayList<>();
            Collections.addAll(arrays, "hello", "world");
        }
        return System.currentTimeMillis() - startTime;
    }

    public static long arraysTest(int count) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            List<String> arrays = new ArrayList<>();
            arrays.addAll(Arrays.asList("hello", "world"));
        }
        return System.currentTimeMillis() - startTime;
    }
}

我们使用这两个方法,向其中添加元素,分别循环一个小目标,得出耗时对比:

arrays.addAll()耗时:3324ms

Collections.addAll()耗时:2034ms

可以看到,这两个方法的耗时差距还是很大的,arrays.addAll()的耗时大概是Collections.addAll()的1.6倍左右。看来Bruce Eckel没有骗我,他是个诚实的人,说的确实对。

3. 为什么它这么快?

好了,是不是的问题已经解决了,我们已经知道,Collections.addAll()确实比arrays.addAll()方法快很多,可是:

Why?

让我们回过头来再看一眼刚才的代码,首先是Collections.addAll()

 List<String> arrays = new ArrayList<>();
 Collections.addAll(arrays, "hello",  "world");
 public static <T> boolean addAll(Collection<? super T> c, T... elements) {
        boolean result = false;
        for (T element : elements)
            result |= c.add(element);
        return result;
    }

这两行代码很简单,就做了两件事情:

  1. 创建arrays
  2. 调用addAll()
  3. 使用数组迭代器,将元素添加到arrays

然后是arrays.addAll()

List<String> arrays = new ArrayList<>();
arrays.addAll(Arrays.asList("hello",  "world"));
public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount
        System.arraycopy(a, 0, elementData, size, numNew);
        size += numNew;
        return numNew != 0;
    }

它做了什么呢?可以看到:

  1. 创建arrays
  2. 使用Arrays.asList()新创建了一个集合
  3. 调用addAll()
  4. 使用c.toArray()方法复制入参到a中。(耗时较长)
  5. 使用ensureCapacityInternal()校验是否溢出
  6. 使用System.arraycopy()拷贝a中新传入的元素到数组容器elementData

综上所述,可以得出,Collections.addAll()arrays.addAll()主要有两种差别:

  1. arrays.addAll()方法会在调用前使用Arrays.asList()多创建一个集合
  2. 在两个addAll()方法内部,arrays.addAll()会比Collections.addAll()多一些集合创建,数组长度校验的操作。

因此,大家在开发过程中,还是尽量使用Collections.addAll(),可以获得更好的性能。

作者:探云观海