Explain Codes LogoExplain Codes Logo

How do I join two lists in Java?

java
list-merging
performance
functional-programming
Alex KataevbyAlex Kataev·Feb 12, 2025
TLDR

Join two lists in a jiffy using List.addAll() for direct merging:

// Creating a list party, inviting list1 and list2 🥳 List<String> combinedList = new ArrayList<>(list1); combinedList.addAll(list2);

On the flip side, use streams from Java 8 and above for a cleaner, non-destructive method:

// List1 and list2 decided to stream together. No lists were hurt in the process 🐳 List<String> combinedList = Stream.concat(list1.stream(), list2.stream()) .collect(Collectors.toList());

In both cases, order of elements is preserved. addAll() modifies list1, but the stream approach generates an entirely new list, leaving the original lists untouched.

Streamlined joining with Java 16

The latest Java releases bring improvements to list merging. From Java 16 and beyond, you can leverage the toList() method for a more concise syntax:

// Java 16 decided to make life easier. Thank you, Java 16 🙌 List<String> combinedList = Stream.concat(list1.stream(), list2.stream()) .toList();

Note that this method produces a new list and the original lists remain unchanged.

Merging lists: A pause for thought

When you're merging lists, consider performance and immutable properties. While addAll() modifies the original list and is generally faster, it may not be the best choice for larger datasets. On the other hand, though stream operations offer a cleaner approach, it could be slower for big data lists.

Put simply:

  • addAll(): Quick and modifies original list.
  • Streams: Ensures immutability but could be slower.

For heavy-duty tasks or if immutable lists are what you seek, try CompositeUnmodifiableList.

Tips for effective list concatenation

  • Using Streams helps encourage functional programming and offers cleaner, more readable code.
  • For handling large datasets, CompositeUnmodifiableList can be a memory-efficient choice.
  • For situations that require concatenating numerous lists dynamically, resort to the flatMap method with Java 9's List.of().
  • Try to keep your code free of third-party dependencies by using native JDK methods.

When to use which method?

addAll() method

  • Best suited for small to medium-sized data operations.
  • When the original list modification is not a concern.
  • When using older Java versions.

Stream concatenation

  • In functional programming.
  • To ensure the original lists remain intact.
  • When code readability is a priority.

Memory-efficient concatenation

  • CompositeUnmodifiableList for high volume data.
  • When you need read-only access to lists.
  • To optimize memory usage and garbage collection.

What about edge cases?

Working with lists can occasionally throw a few surprises:

  • Type compatibility: Ensure the lists you're merging contain elements of compatible types to steer clear of ClassCastException.
  • Null elements: Be mindful, null elements can result in NullPointerException. Handle them appropriately.
  • Concurrency: When multiple threads access lists concurrently, prevent potential problems using synchronized or concurrent collections.

Are there other methods?

While the methods discussed above are quite robust, there are other noteworthy options:

Third-party library functions

Although it's generally recommended to use native JDK functions, popular libraries like Apache Commons Collections and Google's Guava provide utilities for list merging.

Compatibility with really old Java versions

For environments running on Jurassic era Java (JDK 1.3) but Collections.addAll() has been around since Java 1.2:

// Good old fashioned way, like grandma used to do List<String> combinedList = new ArrayList<>(listOne); combinedList.addAll(listTwo);