Back to Blog
Java

Java ArrayList addAll: Usage, Overloads, and Pitfalls

Learn how to use Java ArrayList.addAll: its two overloads, performance tradeoffs, and common pitfalls when combining collections.

JavaArrayListCollectionsaddAllJava Collections Framework
Diagram showing two lists merging into one via the ArrayList addAll method.

If you need to merge two lists in Java, ArrayList.addAll is the direct way to append all elements from a collection to an existing ArrayList. It also supports inserting elements at a specific index. This article explains both overloads, how they behave with different collection types, and the performance tradeoffs you should know.

ArrayList addAll Syntax and Overloads

The ArrayList class provides two overloads of addAll:

boolean addAll(Collection<? extends E> c) boolean addAll(int index, Collection<? extends E> c)

The first appends all elements from c to the end of the list. The second inserts them starting at index, shifting any existing elements to the right. Both return true if the list changed as a result of the call.

Here is a minimal example using Java 9 or later List.of:

List<String> first = new ArrayList<>(List.of("a", "b")); List<String> second = new ArrayList<>(List.of("c", "d")); first.addAll(second); System.out.println(first); // [a, b, c, d]

The second overload:

List<String> list = new ArrayList<>(List.of("a", "b", "e")); list.addAll(1, List.of("c", "d")); System.out.println(list); // [a, c, d, b, e]

The method returns true because the list was modified. If the collection is empty, it returns false.

How addAll Behaves with Different Collection Types

addAll accepts any Collection, not just ArrayList. That includes HashSet, LinkedList, ArrayDeque, or a custom collection. The order of the added elements depends on the iteration order of the source collection. For a HashSet, that order is not guaranteed; for a List, the iteration order is the list's order, and for a LinkedHashSet, it is insertion order.

This flexibility is useful when you need to merge data from multiple sources without converting them to a list first. However, if you rely on a specific order, make sure the source collection provides that guarantee.

Performance Considerations for addAll

The append overload of addAll is O(n), where n is the number of elements in the source collection, because it copies those elements into the target list. The target list may need to grow its internal array to accommodate the new elements; that growth is amortized over multiple additions. If you know the final size in advance, you can pre-size the target with the ArrayList(int initialCapacity) constructor to avoid multiple resizes.

For example:

List<String> target = new ArrayList<>(first.size() + second.size()); target.addAll(first); target.addAll(second);

This is more efficient than starting with an empty default-capacity list and calling addAll twice, which may trigger two resizes. The default capacity is 10, and in common OpenJDK implementations the list grows by 50% each time it exceeds capacity, so repeated additions can cause several array copies.

Common Pitfalls with addAll

One common mistake is adding a list to itself:

List<String> list = new ArrayList<>(List.of("a", "b")); list.addAll(list);

This works without an exception, but the behavior is not obvious. The addAll method copies the source elements through toArray() before inserting them, so it does not loop indefinitely. However, it is a confusing pattern and should be avoided.

Another pitfall is relying on addAll to copy the objects. The target list holds references to the same objects, not deep copies. Adding or removing elements from the source later does not affect the target, but if the source contains mutable objects, changing one of those objects is visible through both collections.

Null handling: addAll throws NullPointerException if the collection argument is null. It does not add null elements unless the collection itself contains nulls.

Adding at a Specific Index: Index Overload

The index-based overload addAll(int index, Collection<? extends E> c) inserts elements at the given position. The index must be between 0 and the current size, inclusive. If it is out of bounds, IndexOutOfBoundsException is thrown.

This overload is useful when you need to merge a collection into a specific position without creating a new list. For example:

List<Integer> numbers = new ArrayList<>(List.of(1, 2, 5)); numbers.addAll(2, List.of(3, 4)); // numbers becomes [1, 2, 3, 4, 5]

The existing elements from the index onward are shifted to the right. The worst-case time is O(size of the target list + size of the added collection) because of the shifting and copying.

Alternatives to addAll for Combining Lists

While addAll is the most direct way to combine lists, there are other approaches depending on your needs. If you want a new list without modifying the originals, you can use:

List<String> combined = new ArrayList<>(first); combined.addAll(second);

Or, with Java 16 or later:

List<String> combined = Stream.concat(first.stream(), second.stream()).toList();

The stream approach is more functional but can be less direct; use it when you prefer a declarative pipeline over mutating an existing list. If you need an immutable result, build a mutable list first and then pass it to List.copyOf (Java 10 or later):

List<String> combined = new ArrayList<>(first); combined.addAll(second); List<String> immutable = List.copyOf(combined);

Maintaining Type Safety with addAll

The signature Collection<? extends E> ensures that you can add a collection of a subtype. For example, if E is Number, you can add a List<Integer> because Integer extends Number. This is a common source of confusion with generics. The wildcard ? extends E allows covariance, which is necessary because Collection<Integer> is not a subtype of Collection<Number>.

If you try to add a collection of a supertype, you'll get a compile-time error. For example, adding a List<Object> to a List<String> is not allowed because Object is not a subtype of String.

Java ArrayList addAll: Append, Insert, and Avoid Common Pitfalls | RYUSLOG DEV