85

If I want to make two lists into one in Java, I can use ListUtils.union(List list1,List list2). But what if I want to combine multiple lists?

This works:

import org.apache.commons.collections.ListUtils;
List<Integer>list1=Arrays.asList(1,2,3);
List<Integer>list2=Arrays.asList(4,5,6);
List<Integer>list3=Arrays.asList(7,8,9);
List<Integer>list4=Arrays.asList(10,0,-1);
System.out.println(ListUtils.union(ListUtils.union(list1, list2),ListUtils.union(list3, list4)));

But it doesn't really look like the best solution, neither is it particularly great to read. Sadly ListUtils.union(list1,list2,list3,list4) doesn't work. Using addAll multiple times and creating its own list just for that with duplicates of all the entries also doesn't seem ideal to me. So what can I do instead?

6
  • 3
    ListUtils is not a standard Java API class. Are you referring to the apache-commons ListUtils class? If so, please edit your question to make that clear. Commented Jul 24, 2017 at 13:10
  • This is a duplicate of Combine multiple Collections into a single logical Collection?. It was already closed as a dupe once, but for some reason reopened. Commented Jul 24, 2017 at 13:19
  • 2
    @MickMnemonic No it is not. I didn't reopen it - I think Ghostcat did himself after he realized this isn't a correct duplicate. If someone asks how to combine Lists without mentioning a particular library, then a question that asks and is answered how to combine Iterables using Guava is not a correct duplicate. Commented Jul 24, 2017 at 13:22
  • @ErwinBolwidt, again, if you look at the answers in the linked question, only one of them uses Guava, and there are native Java 8/7 solutions as well. The question is also the first Google hit for "Combine multiple lists in Java". I don't see how this question is not a duplicate. Maybe you can write an answer to prove me wrong? Commented Jul 24, 2017 at 13:29
  • @ErwinBolwidt I didn't know that it's not standard Java. I use an Eclipse version from a company intranet page, adjusted to my needs. I don't know all details of it. Tomorrow I'll look at the import. Commented Jul 24, 2017 at 17:29

7 Answers 7

176

Java 8 has an easy way of doing it with the help of Stream API shown in the code below. We have basically created a stream with all the lists , and then as we need the individual contents of the lists, there is a need to flatten it with flatMap and finally collect the elements in a List.

List<Integer>list1=Arrays.asList(1,2,3);
List<Integer>list2=Arrays.asList(4,5,6);
List<Integer>list3=Arrays.asList(7,8,9);
List<Integer>list4=Arrays.asList(10,0,-1);
List<Integer> newList = Stream.of(list1, list2, list3,list4)
                                      .flatMap(Collection::stream)
                                      .collect(Collectors.toList());       
 System.out.println(newList); // prints [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, -1]
Sign up to request clarification or add additional context in comments.

4 Comments

Someone seems to have purged the comments here for some reason. Here is an archive: web.archive.org/web/20170725052617/https://stackoverflow.com/… Also, I remember someone saying "this is a lot of overhead", which was probably meant to point out that the conversions to a stream and back are expensive operations. For performance it's much better to use addAll, especially because adding many elements to one list can't be multithreaded effectively. Wow, I learned so much since I asked this question!
To remove duplicated items use Collectors.toSet() instead of list..
"easy"? Easier than implementing the merge yourself, true...
It is actually better even when you merge only two lists, because Stream API is typesafe, but ListUtils.union() uses List instead of List<T> in its signature.
28

Adding other alternatives:

OPTION 1:

List<Integer> joinedList = joinLists(list1, list2, list3, list4);

public static <T> List<T> joinLists(List<T>... lists) {
        return Arrays.stream(lists).flatMap(Collection::stream).collect(Collectors.toList()); 
}

OPTION 2:

List<Integer> joinedList = new ArrayList<>();
Stream.of(list1, list2, list3, list4).forEach(joinedList::addAll);

3 Comments

I like this better than the suggested answer, even though it is very similar, because it's cleaner. Using the stream api and a reduce function this could even be more streamlined, so the separate "new ArrayList" is not necessary anymore.
Note, that if you have huge number of elements in lists, your references are held in memory multiple times. Have a look at using original lists for retrieving data here stackoverflow.com/a/13868352/11152683
I chose a combination of option 1 and 2 with a java 16 flavor: Stream.of(list1, list2, list3, list4).flatMap(Collection::stream).toList();
2

Use an ArrayList to list down all your Lists....

ArrayList<String> arrl = new ArrayList<String>();
List<String> list1 = new ArrayList<String>();
    list.add("one");
    list.add("two");
List<String> list2 = new ArrayList<String>();
    list.add("one1");
    list.add("two2");
    arrl.addAll(list1);
arrl.addAll(list2);
    System.out.println("After Copy: "+arrl);

Thats it your list will be made

Comments

2

You can write your own methods to merge two or more lists. Example:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test{ 
    public static void main(String[] args) {
        List<Integer>list1 = Arrays.asList(1,2,3);
        List<Integer>list2 = Arrays.asList(4,5,6);
        List<Integer>list3 = Arrays.asList(7,8,9);
        List<Integer>list4 = Arrays.asList(10,0,-1);

        System.out.println(combineMyLists(list1,list2,list3,list4));
        System.out.println("----------------------");
        System.out.println(combineMyLists2(list1,list2,list3,list4));
    } 
    private static List<Integer> combineMyLists(List<Integer>... args) {
        List<Integer> combinedList = new ArrayList<>();
        for(List<Integer> list : args){
            for(Integer i: list){
               combinedList.add(i);
            }
        }
        return combinedList;
    }
    private static List<Integer> combineMyLists2(List<Integer>... args) {
        List<Integer> combinedList = Stream.of(args).flatMap(i -> i.stream()).collect(Collectors.toList());   ;
        return combinedList;
    }
}

Comments

0

If concatenating many lists, or large lists, the origin lists have an efficient toArray() implementation, and the destination list has a way to request an explicit size, and an efficient addAll() implementation, you can save a lot of time over a simple toList(). ArrayList, probably the most commonly used List, has all three of these qualities.

Calling the sized ArrayList constructor performs a single allocation, and using addAll() eliminates the need to inefficiently copy elements into the destination List one at a time.

    @SafeVarargs
    public static <T> List<T> concatListsJava8(List<T>... lists) {
        int totalSize = Math.toIntExact(
                Arrays.stream(lists)
                .mapToLong(List::size)
                .sum());

        List<T> newList = new ArrayList<>(totalSize);

        Arrays.asList(lists).forEach(newList::addAll);

        return newList;
    }

    public static <T> List<T> concatListsJava5(List<T>... lists) {
        final int numLists = lists.length;
        int totalSize = 0;
        for (int i = 0; i < numLists; ++i) {
            totalSize += lists[i].size();
            if (totalSize < 0) {
                throw new ArithmeticException("integer overflow");
            }
        }

        List<T> newList = new ArrayList<T>(totalSize);

        for (int i = 0; i < numLists; ++i) {
            newList.addAll(lists[i]);
        }
        return newList;
    }

3 Comments

Why do you cast to Long and then back to Integer? Does the sum method not exist for integers?
Does the accepted answer keep reallocating new lists? I would not expect that from these streams.
@FabianRöling 1. The long <-> int conversion is to detect overflow if the lists exceed Integer.MAX_VALUE in total size. 2. It's not just about reallocating lists, it's also about one-at-a-time vs bulk copying. SpinedBuffer allocates very efficiently (I will edit the answer to clarify), but it also accepts one element per method call. Preallocating an ArrayList and calling addAll converts each input List to an array and uses System.arraycopy() to bulk copy elements into its internal array, which is blazingly fast.
0

There is another possibility doing double brace initialization

var combinedList = new ArrayList<>() {{
    addAll(List.of("a", "b", "c"));
    addAll(List.of("d", "e", "f"));
    addAll(List.of("g", "h", "i"));

    add("j);
}};

It's considered an anti-pattern (depending on how you use it), but I still think there are some use cases where it is valid and makes your life easier (tests, static configurations, manual constructing of JSON schemas, etc).

You can read more about it here https://www.baeldung.com/java-double-brace-initialization

and here is a description of the potential memory leak case, although I do not know if it is a relict of the past or still a thing with newer GC versions https://stackoverflow.com/a/27521360/2324388

4 Comments

This should not be a recommendation. Double brace initialization is very bad and should not be used even in the cases mentioned.
@akourt, could you elaborate on why it can be considered very bad for so long and still be part of the JDK, even if the memory leak issue is known for 15+ years and probably fixed in the meantime? It's not even deprecated
Don't know if the memory leak has been addressed but the creation of redundant inner classes has not, so why use this where there are better ways to achieve this.
@akourt so why is that a problem? You won't notice the inner class, neither performance-wise nor visually, especially in the scenarios I've described. On the other hand you gain a compact way of initializing a collection as one block, without creating an extra method somewhere. You can assign it directly to a static field for example.
0

I find .concat more readable, and it eliminates the need for a temporary variable.

Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.