简体   繁体   中英

Why is Collections.addAll supposed to be faster than c.addAll

The Java API docs say the following about Collections.addAll

The behavior of this convenience method is identical to that of c.addAll(Arrays.asList(elements)), but this method is likely to run significantly faster under most implementations.

So if I understand correctly, a) is slower than b):

a)

Collection<Integer> col = new ArrayList<Integer>();
col.addAll(Arrays.asList(1, 2, 3, 4, 5));

b)

Collection<Integer> col = new ArrayList<Integer>();
// Collections.addAll(col, Arrays.asList(1, 2, 3, 4, 5)); <-- won't compile
Collections.addAll(col, 1, 2, 3, 4, 5);

Can anyone explain to me, why that is?

edited: corrected code example. thx to polygenelubricants

Let's take a closer look at the two of them:

 // a) col.addAll(Arrays.asList(1, 2, 3, 4, 5)); 

Here's what happens:

  1. varags + autoboxing creates Integer[]
  2. Arrays.asList creates a List<Integer> backed by the array
  3. addAll iterates over a Collection<Integer> using Iterator<Integer>
 // b) Collections.addAll(col, 1, 2, 3, 4, 5); 

Here's what happens:

  1. varargs + autoboxing creates Integer[]
  2. addAll iterates over an array (instead of an Iterable<Integer> )

We can see now that b) may be faster because:

  • Arrays.asList call is skipped, ie no intermediary List is created.
  • Since the elements are given in an array (thanks to varargs mechanism), iterating over them may be faster than using Iterator .

That said, unless profiling shows otherwise, the difference isn't likely to be "significant". Do not optimize prematurely. While Java Collection Framework classes may be slower than arrays, they perform more than adequately for most applications.

API links

See also

Related questions


Summary

  • If you're adding elements from an array, you can use Collections.addAll(col, arr)
    • Remember that varargs are also done using arrays
  • If you're adding elements from a Collection , use col.addAll(otherCol)
    • Do NOT eg Collections.addAll(col, otherCol.toArray())
      • Such roundabout way is likely to be slower!
  • It's not that one is supremely faster than the other
    • It's about skipping unnecessary steps given the current situation

The only reason it might be faster is that it avoids the call to Arrays.asList which should be relatively cheap since it just wraps the array. Some Collection implementations, for example LinkedList convert the passed collection back to an array before adding the elements, causing additional overhead.

On the other hand, ArrayList.addAll allocates the needed space once before adding any elements and so should be much faster when Collections.addAll requires multiple resizing of the backing array.

In summary, Collections.addAll could be faster when repeatedly adding only a few elements to a collection, but I doubt that this case would ever be a performance bottleneck.

(Let's build on SE Platform 6)

It all depends on actual collection implementation. In your example we have

Collection<Integer> col = new ArrayList<Integer>();

and addAll method in ArrayList is overriden. No iterations whatsoever. Here's the source:

public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
    int numNew = a.length;
ensureCapacity(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
return numNew != 0;
}

As you might notice c.toArray() also depends on actual implementation. Again, in your case Arrays.asList() results in ArrayList which one's version of toArray() method looks like this:

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

This static method is based on System.arraycopy

So actually what we deal here with is two calls of System.arraycopy which is not that bad actually because it's a native method, specifically optimized for current operation system.

So, to sum it all up in Mr. polygenelubricants' style:

  1. varags + autoboxing creates Integer[]
  2. Arrays.asList creates an ArrayList<Integer>
  3. ArrayList.addAll calls System.arraycopy(size) x2, size = 5

In your case of 5 objects in the array Collections.addAll is of cource faster. BUT irrelevant with such a small array size. On the other hand if it was, say, 100k elements in an array then col.addAll(Arrays.asList(...)) is much more efficient 'cause with native method it is a single memcpy/memmove we dealing with as opposed to 100k iterations/copy operations.

And again, it all depends on collection's implementation. LinkedList for example will iterate over it as was expected.

Here are the (approximate) associated time complexity cost functions for each of the steps mentioned by @polygenelubricants:

a) 3 iterations over arguments list ~= C(3N)

b) 2 iterations over arguments list ~= C(2N)

Clearly they are both O(N) but approach b) saves ~N comparisons over approach a). Hopefully this is helpful to anyone who was interested in a quantitative explanation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM