1067

Is there a simpler way than:

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);

Conditions:

  1. Do not modify the original lists.
  2. JDK only.
  3. No external libraries.

Bonus points for a one-liner or a JDK 1.3 version.

3

33 Answers 33

1063

In Java 8:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream())
                             .collect(Collectors.toList());

Java 16+:

List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).toList();
10
  • 152
    Gawd, that's a thing in Java 8? Technically you win I guess, but that's a heck of a long line :-) Sep 9, 2013 at 20:03
  • 6
    For the casual reader, here is a shorter solution using also Java _ Streams: stackoverflow.com/a/34090554/363573
    – Stephan
    Oct 4, 2016 at 13:16
  • 7
    It is ugly but at least its fluent and can be used without multi line lambdas. I really wish there was a fluent addAll that returned the concatinated list. Jan 11, 2018 at 15:06
  • 21
    I guess it's worth noting that it's really easy to get a distinct list out of this too, like so: List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
    – Mr Baloon
    Dec 30, 2018 at 19:18
  • 18
    Alternative to concat: stream of streams Stream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList()) Apr 9, 2019 at 11:17
690

Off the top of my head, I can shorten it by one line:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
5
  • 195
    While you're technically correct, you have shortened it by one line, the asymmetry of this bugs me. Enough that I'm happier to "spend" the extra line. Dec 15, 2011 at 0:54
  • 17
    Isn't there a problem here where the newList's interal array will be initialized to the size of listOne and then have to potentially expand when adding all of the items from listTwo? Would it be better to take the size of each list and use that to size the new array?
    – Eric
    Mar 2, 2016 at 8:38
  • 3
    This was the solution that worked best for me. I made a comparison on the performance of different solutions this came out winner, together with creating the empty list and then addAll() of both. I tried all those that suggest not copying the lists and they result having a lot of overhead we didn't need this time. Sep 26, 2016 at 6:46
  • Use a LinkedList over ArrayList for efficient adding. stackoverflow.com/a/322742/311420 Apr 7, 2020 at 14:49
  • this does not work with generic value types, I am using Java 17: List<? extends BaseEntity> toManyEntities = new ArrayList<>(); List<? extends BaseEntity> toManyInsertEntities = new ArrayList<>(); List<? extends BaseEntity> toManyUpdateEntities = new ArrayList<>(); toManyEntities = toManyInsertEntities; toManyEntities.addAll(toManyUpdateEntities); -> this cannot compile But this works, and is mentioned sort-of in the answer just above your answer: toManyEntities = Stream.concat(toManyInsertEntities.stream(),toManyUpdateEntities.stream()).collect(Collectors.toList());
    – Ricardo
    Jul 8, 2022 at 9:11
445

You could use the Apache commons-collections library:

List<String> newList = ListUtils.union(list1, list2);
10
  • 73
    Nice, but requires apache commons. He did specify 'no external libraries'
    – Quantum7
    Apr 26, 2011 at 1:17
  • 128
    @Quantum7, still useful for other people ;) Also, is apache commons even an external library? I don't start anything without it!
    – tster
    Mar 30, 2012 at 23:31
  • 31
    @Platinum No, according to the docs ListUtils.union is exactly equivalent to OP's code. But perhaps it is misleading to use a SET operation ("Union") in a list context. I can see how you might expect this to remove duplicates or something, but it appears the method does not do that.
    – Quantum7
    Dec 17, 2012 at 23:52
  • 31
    Avoid Apache Commons Collections. It’s not typesafe, there are no generics. Great if you use Java 1.4, but for Java 5 and above, I’d prefer Google Guava. Sep 8, 2013 at 19:37
  • 18
    @MichaelPiefel The latest Apache Commons Collections 4 is type-safe. With Java 8 method reference, this kind of static utilities become very important.
    – mingfai
    Jan 21, 2014 at 18:17
251

Another Java 8 one-liner:

List<String> newList = Stream.of(listOne, listTwo)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());

As a bonus, since Stream.of() is variadic, you may concatenate as many lists as you like.

List<String> newList = Stream.of(listOne, listTwo, listThree)
                            .flatMap(Collection::stream)
                            .collect(Collectors.toList());
6
  • 45
    x -> x.stream() could be replaced with Collection::stream.
    – Martin
    May 29, 2015 at 16:18
  • 22
    ...or even with List::stream.
    – MC Emperor
    Jan 11, 2019 at 10:34
  • Nice. I like this approach. May 4, 2021 at 5:55
  • Super. Also null safe. Both lists can be null and it would still work ! Feb 14, 2022 at 14:13
  • 3
    In JDK 17, .collect(Collectors.toList()) can be replaced with just .toList();
    – k_rollo
    Feb 18, 2023 at 0:22
101

One of your requirements is to preserve the original lists. If you create a new list and use addAll(), you are effectively doubling the number of references to the objects in your lists. This could lead to memory problems if your lists are very large.

If you don't need to modify the concatenated result, you can avoid this using a custom list implementation. The custom implementation class is more than one line, obviously...but using it is short and sweet.

CompositeUnmodifiableList.java:

public class CompositeUnmodifiableList<E> extends AbstractList<E> {

    private final List<? extends E> list1;
    private final List<? extends E> list2;

    public CompositeUnmodifiableList(List<? extends E> list1, List<? extends E> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }
    
    @Override
    public E get(int index) {
        if (index < list1.size()) {
            return list1.get(index);
        }
        return list2.get(index-list1.size());
    }

    @Override
    public int size() {
        return list1.size() + list2.size();
    }
}

Usage:

List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);
3
  • 13
    This is a workable solution, but do note that if the underlying list objects change (list1, list2), the contents of this list change. You may not be able to modify an instance of the CompositeUnmodifiableList itself but if you can get a reference to the original lists, then you can. Also for those that are unfamiliar: the final modifier just affects the reference to the list object itself can't change but it's still possible for the contents of the list to change!
    – jwj
    Sep 16, 2014 at 3:34
  • 3
    @jwj all very good points, thank you. The class name probably deserves some explanation. I see this class as doing something very similar to the Collections.unmodifiableList() method, which wraps a list to make it unmodifiable. CompositeUnmodifiableList does the same thing, except it wraps two lists and provides a concatenated view. All the points you make about CompositeUnmodifiableList are also true of Collections.unmodifiableList() as well.
    – Kevin K
    Feb 23, 2015 at 16:22
  • 2
    The constructor can take List<? extends E> Feb 5, 2017 at 13:48
97

Probably not simpler, but intriguing and ugly:

List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };

Don't use it in production code... ;)

11
  • 55
    Ugly and evil, just as almost any use of double brace initialization. It ís shorter, though ;)
    – Jorn
    Aug 15, 2009 at 20:50
  • 5
    @MarnixKlooster: Eclipse knows that you should not use it and makes it unpleasant to use ;-) Oct 3, 2011 at 7:06
  • 23
    Though it is physically one line, I do not consider this a "one-liner".
    – splungebob
    Dec 13, 2012 at 21:28
  • 12
    why do people hate anonymous block initializers Dec 21, 2012 at 13:01
  • 21
    @NimChimpsky I think it's mostly because it's not just an anonymous block initializer, but you're actually creating an anonymous subclass of ArrayList. That being said, if you trust the results of this Double Brace Initilization question, it makes it seem like hating DBI is mostly a matter of stylistic taste and micro-optimization. As far as I can tell, there are no major penalties for doing it. The sneaky downside would be if you ever tried to compare its class because it won't be ArrayList.
    – Patrick
    Dec 27, 2012 at 17:15
90

Not simpler, but without resizing overhead:

List<String> newList = new ArrayList<>(listOne.size() + listTwo.size());
newList.addAll(listOne);
newList.addAll(listTwo);
1
  • I think it's the correct and the most efficient answer here. I just don't understand why Java doesn't provide a constructor or a static method for it. It isn't the second day of Java in this world, yet such basic things are just missing. So disappointing and triggering at the same time.
    – Frank
    Jul 18, 2023 at 8:33
59

Found this question looking to concatenate arbitrary amount of lists, not minding external libraries. So, perhaps it will help someone else:

com.google.common.collect.Iterables#concat()

Useful if you want to apply the same logic to a number of different collections in one for().

2
  • 12
    For example: Lists.newArrayList(Iterables.concat(list1,list2));
    – meilechh
    Feb 13, 2014 at 17:14
  • you should call com.google.common.collect.Iterators#concat(java.util.Iterator<? extends java.util.Iterator<? extends T>>) instead of Iterables#concat(); because the later still copy elements into temp link!
    – bob
    Apr 14, 2016 at 6:36
55

Java 8 (Stream.of and Stream.concat)

The proposed solution is for three lists though it can be applied for two lists as well. In Java 8 we can make use of Stream.of or Stream.concat as:

List<String> result1 = Stream.concat(Stream.concat(list1.stream(),list2.stream()),list3.stream()).collect(Collectors.toList());
List<String> result2 = Stream.of(list1,list2,list3).flatMap(Collection::stream).collect(Collectors.toList());

Stream.concat takes two streams as input and creates a lazily concatenated stream whose elements are all the elements of the first stream followed by all the elements of the second stream. As we have three lists we have used this method (Stream.concat) two times.

We can also write a utility class with a method that takes any number of lists (using varargs) and returns a concatenated list as:

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

Then we can make use of this method as:

List<String> result3 = Utils.concatenateLists(list1,list2,list3);
0
52

Here is a java 8 solution using two lines:

List<Object> newList = new ArrayList<>();
Stream.of(list1, list2).forEach(newList::addAll);

Be aware that this method should not be used if

  • the origin of newList is not known and it may already be shared with other threads
  • the stream that modifies newList is a parallel stream and access to newList is not synchronized or threadsafe

due to side effect considerations.

Both of the above conditions do not apply for the above case of joining two lists, so this is safe.

Based on this answer to another question.

5
  • 18
    If I am not wrong this is actually not recommended - docs.oracle.com/javase/8/docs/api/java/util/stream/… Please see side -effects section. > Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement, as well as other thread-safety hazards. So in this case it is better to use Collectors.toList() Mar 14, 2017 at 15:31
  • @AntonBalaniuc Question is if whether this is really a side effect. At that point newList is not observable by any other thread. But you are correct that this probably shouldn't be done if it is unknown where the value of newListcame from (for example if newList was passed as a parameter. Jul 11, 2017 at 5:45
  • 2
    I'm curious; why .forEach(newList::addAll); instead of .collect(Collectors.toList());?
    – 11684
    Aug 22, 2017 at 10:16
  • 5
    @11684 because the collector would collect a List<List<Object>>. What you may have in mind is something like this: stackoverflow.com/questions/189559/… Aug 22, 2017 at 11:23
  • @SpaceTrucker Oops, I overlooked that. Thanks for clearing up my confusion. Yes, I should have been thinking of flatMap.
    – 11684
    Aug 22, 2017 at 11:26
39

This is simple and just one line, but will add the contents of listTwo to listOne. Do you really need to put the contents in a third list?

Collections.addAll(listOne, listTwo.toArray());
2
  • 15
    Not modifying the original lists was one of the criteria, but this is useful to have here as an example for situations where that's not a constraint. Sep 9, 2013 at 20:01
  • 10
    Thanks, or even simpler listOne.addAll(listTwo)
    – Jay
    Jan 3, 2020 at 16:23
37

Slightly simpler:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
2
  • Would this cause duplicated Strings? Meaning a String that exists in both list will exist twice in the resulting list?
    – AgentKnopf
    Jun 13, 2012 at 10:33
  • 6
    @Zainodis Yes, there could be duplicates. The List structure imposes no uniqueness constraints. You can remove dupes by doing the same thing with sets. Set<String> newSet = new HashSet<>(setOne); newSet.addAll(setTwo);
    – Patrick
    Dec 27, 2012 at 17:01
22

A little shorter would be:

List<String> newList = new ArrayList<String>(listOne);
newList.addAll(listTwo);
0
21

You can create your generic Java 8 utility method to concat any number of lists.

@SafeVarargs
public static <T> List<T> concat(List<T>... lists) {
    return Stream.of(lists).flatMap(List::stream).collect(Collectors.toList());
}
14

In Java 8 (the other way):

List<?> newList = 
Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList());
1
13

You can do a oneliner if the target list is predeclared.

(newList = new ArrayList<String>(list1)).addAll(list2);
0
10

another one liner solution using Java8 stream, since flatMap solution is already posted, here is a solution without flatMap

List<E> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);

or

List<E> ints = Stream.of(list1, list2).collect(ArrayList::new, List::addAll, List::addAll);

code

    List<List<Integer>> lol = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
    List<Integer> li = lol.stream().collect(ArrayList::new, List::addAll, List::addAll);
    System.out.println(lol);
    System.out.println(li);

output

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]
2
  • 1
    I would add that this solution is probably more performant than the one using flatMap, because the lists are only iterated once when they are collected May 23, 2017 at 9:01
  • I dont think flatMap causes additional iteration, could you clarify?
    – wilmol
    Mar 30, 2021 at 21:05
9

We can join 2 lists using java8 with 2 approaches.

    List<String> list1 = Arrays.asList("S", "T");
    List<String> list2 = Arrays.asList("U", "V");

1) Using concat :

    List<String> collect2 = Stream.concat(list1.stream(), list2.stream()).collect(toList());
    System.out.println("collect2 = " + collect2); // collect2 = [S, T, U, V]

2) Using flatMap :

    List<String> collect3 = Stream.of(list1, list2).flatMap(Collection::stream).collect(toList());
    System.out.println("collect3 = " + collect3); // collect3 = [S, T, U, V]
1
  • 3
    When answering an eleven year old question with thirty other answers be sure to point out what new aspects of the question your answer addresses, and to note if these techniques would have worked when the question was asked, or if they depend on features that have been introduced over the years. Mar 25, 2020 at 18:05
8

The smartest in my opinion:

/**
 * @param smallLists
 * @return one big list containing all elements of the small ones, in the same order.
 */
public static <E> List<E> concatenate (final List<E> ... smallLists)
{
    final ArrayList<E> bigList = new ArrayList<E>();
    for (final List<E> list: smallLists)
    {
        bigList.addAll(list);
    }
    return bigList;
}
2
  • 5
    Don't forget the @SafeVarargs! Dec 7, 2014 at 16:00
  • The bigList might get resized potentially. So the solution is functionally correct, but might not the most efficient.
    – Frank
    Jul 18, 2023 at 8:39
8

Almost of answers suggest to use an ArrayList.

List<String> newList = new LinkedList<>(listOne);
newList.addAll(listTwo);

Prefer to use a LinkedList for efficient add operations.

ArrayList add is O(1) amortized, but O(n) worst-case since the array must be resized and copied. While LinkedList add is always constant O(1).

more infos https://stackoverflow.com/a/322742/311420

2
  • 3
    Without performance benchmarks, this is questionable advice, especially since your argument about add does not refer to the bulk addAll operation. Apr 15, 2021 at 21:08
  • "Prefer to use a LinkedList for efficient add operations." Unless you know how many elements you have, and able to create the ArrayList with a proper initial capacity. In this case, using new ArrayList<>(list1.size() + list2.size()); would be extremely more efficient than even using addAll twice.
    – David
    Mar 23 at 3:22
7

You could do it with a static import and a helper class

nb the generification of this class could probably be improved

public class Lists {

   private Lists() { } // can't be instantiated

   public static List<T> join(List<T>... lists) {
      List<T> result = new ArrayList<T>();
      for(List<T> list : lists) {
         result.addAll(list);
      }
      return results;
   }

}

Then you can do things like

import static Lists.join;
List<T> result = join(list1, list2, list3, list4);
2
  • How is the static import or the helper class relevant?
    – shmosel
    May 30, 2019 at 21:02
  • I think the method signature will be like - 'public static <T> List<T> join'. And 'return result' Jul 13, 2022 at 3:12
6

Java 8 version with support for joining by object key:

public List<SomeClass> mergeLists(final List<SomeClass> left, final List<SomeClass> right, String primaryKey) {
    final Map<Object, SomeClass> mergedList = new LinkedHashMap<>();

    Stream.concat(left.stream(), right.stream())
        .map(someObject -> new Pair<Object, SomeClass>(someObject.getSomeKey(), someObject))
        .forEach(pair-> mergedList.put(pair.getKey(), pair.getValue()));

    return new ArrayList<>(mergedList.values());
}
4
public static <T> List<T> merge(List<T>... args) {
    final List<T> result = new ArrayList<>();

    for (List<T> list : args) {
        result.addAll(list);
    }

    return result;
}
4

Use a Helper class.

I suggest:

public static <E> Collection<E> addAll(Collection<E> dest, Collection<? extends E>... src) {
    for(Collection<? extends E> c : src) {
        dest.addAll(c);
    }

    return dest;
}

public static void main(String[] args) {
    System.out.println(addAll(new ArrayList<Object>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));

    // does not compile
    // System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList("a", "b", "c")));

    System.out.println(addAll(new ArrayList<Integer>(), Arrays.asList(1,2,3), Arrays.asList(4, 5, 6)));
}
4
public static <T> List<T> merge(@Nonnull final List<T>... list) {
    // calculate length first
    int mergedLength = 0;
    for (List<T> ts : list) {
      mergedLength += ts.size();
    }

    final List<T> mergedList = new ArrayList<>(mergedLength);

    for (List<T> ts : list) {
      mergedList.addAll(ts);
    }

    return mergedList;
  }
3

My favourite way, using fluent api and Guava:

List<String> combined = ImmutableList.<String>builder().addAll(list1).addAll(list2).build()
0

No way near one-liner, but I think this is the simplest:

List<String> newList = new ArrayList<String>(l1);
newList.addAll(l2);

for(String w:newList)
        System.out.printf("%s ", w);
0

Here's an approach using streams and java 8 if your lists have different types and you want to combine them to a list of another type.

public static void main(String[] args) {
    List<String> list2 = new ArrayList<>();
    List<Pair<Integer, String>> list1 = new ArrayList<>();

    list2.add("asd");
    list2.add("asdaf");
    list1.add(new Pair<>(1, "werwe"));
    list1.add(new Pair<>(2, "tyutyu"));

    Stream stream = Stream.concat(list1.stream(), list2.stream());

    List<Pair<Integer, String>> res = (List<Pair<Integer, String>>) stream
            .map(item -> {
                if (item instanceof String) {
                    return new Pair<>(0, item);
                }
                else {
                    return new Pair<>(((Pair<Integer, String>)item).getKey(), ((Pair<Integer, String>)item).getValue());
                }
            })
            .collect(Collectors.toList());
}
0

If you want to do this statically you can the following.

The examples uses 2 EnumSets in natural-order (==Enum-order) A, B and joins then in an ALL list.

public static final EnumSet<MyType> CATEGORY_A = EnumSet.of(A_1, A_2);
public static final EnumSet<MyType> CATEGORY_B = EnumSet.of(B_1, B_2, B_3);

public static final List<MyType> ALL = 
              Collections.unmodifiableList(
                  new ArrayList<MyType>(CATEGORY_A.size() + CATEGORY_B.size())
                  {{
                      addAll(CATEGORY_A);
                      addAll(CATEGORY_B);
                  }}
              );
1
  • This would create a new annonymous class. Not recommended approach!
    – kravemir
    Jan 25, 2019 at 10:00
0

I'm not claiming that it's simple, but you mentioned bonus for one-liners ;-)

Collection mergedList = Collections.list(new sun.misc.CompoundEnumeration(new Enumeration[] {
    new Vector(list1).elements(),
    new Vector(list2).elements(),
    ...
}))

Edit: ... and 13 years later

var mergedList = Stream.of(list1, list2)
                       .flatMap(Collection::stream)
                       .toList()
6
  • why should someone never use those?
    – David
    Jun 3, 2013 at 16:27
  • 6
    @David because it aimed to be used internally in JDK. If you used that in your code, your code will quite probably not running on non-Sun (or non-Oracle now) JDK/JRE. Jul 23, 2013 at 7:06
  • @AdrianShum Are there other JDKs/JREs than Oracle? That would surprise me. Even if limited to the most common API functionality, rebuilding that whole stuff would probably take ages...
    – Egor Hans
    Sep 2, 2017 at 10:52
  • 1
    There is quite a lot of JVM. Most commonly seen one in enterprise world should be the IBM one which is, iirc, bundled with websphere Sep 2, 2017 at 11:41
  • whenever you see usages of Vector, take a 5 minute break.
    – bvdb
    Jul 22, 2020 at 15:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.