ChatGPT解决这个技术问题 Extra ChatGPT

How do I join two lists in Java?

Conditions: do not modify the original lists; JDK only, no external libraries. Bonus points for a one-liner or a JDK 1.3 version.

Is there a simpler way than:

List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);
If you are doing this solely for iteration purposes see another question - there are google guava and java 8 solutions stackoverflow.com/questions/4896662/…
Java 8 solution with utility method: stackoverflow.com/a/37386846/1216775

Z
ZhekaKozlov

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();

Gawd, that's a thing in Java 8? Technically you win I guess, but that's a heck of a long line :-)
For the casual reader, here is a shorter solution using also Java _ Streams: stackoverflow.com/a/34090554/363573
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.
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());
Alternative to concat: stream of streams Stream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
A
AdamC

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

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

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.
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?
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.
Use a LinkedList over ArrayList for efficient adding. stackoverflow.com/a/322742/311420
this does not work with generic value types, I am using Java 17: List toManyEntities = new ArrayList<>(); List toManyInsertEntities = new ArrayList<>(); List 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());
N
Neuron

You could use the Apache commons-collections library:

List<String> newList = ListUtils.union(list1, list2);

Nice, but requires apache commons. He did specify 'no external libraries'
@Quantum7, still useful for other people ;) Also, is apache commons even an external library? I don't start anything without it!
@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.
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.
@MichaelPiefel The latest Apache Commons Collections 4 is type-safe. With Java 8 method reference, this kind of static utilities become very important.
L
Littm

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());

x -> x.stream() could be replaced with Collection::stream.
...or even with List::stream.
Nice. I like this approach.
Super. Also null safe. Both lists can be null and it would still work !
u
ursa

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);

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 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.
The constructor can take List<? extends E>
T
Tim Cooper

Probably not simpler, but intriguing and ugly:

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

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


Ugly and evil, just as almost any use of double brace initialization. It ís shorter, though ;)
@MarnixKlooster: Eclipse knows that you should not use it and makes it unpleasant to use ;-)
Though it is physically one line, I do not consider this a "one-liner".
why do people hate anonymous block initializers
@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.
M
Martin

Not simpler, but without resizing overhead:

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

Y
Yuri Geinish

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().


For example: Lists.newArrayList(Iterables.concat(list1,list2));
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!
a
akhil_mittal

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);

S
SpaceTrucker

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.


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()
@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.
I'm curious; why .forEach(newList::addAll); instead of .collect(Collectors.toList());?
@11684 because the collector would collect a List<List<Object>>. What you may have in mind is something like this: stackoverflow.com/questions/189559/…
@SpaceTrucker Oops, I overlooked that. Thanks for clearing up my confusion. Yes, I should have been thinking of flatMap.
c
ceklock

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());

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.
Thanks, or even simpler listOne.addAll(listTwo)
X
Xaerxess

Slightly simpler:

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

Would this cause duplicated Strings? Meaning a String that exists in both list will exist twice in the resulting list?
@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);
J
Jorn

A little shorter would be:

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

D
Daniel Hári

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());
}

d
deterb

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

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

n
noufalcep

In Java 8 (the other way):

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

This approach is already suggested by this answer: stackoverflow.com/a/37386846
S
Saravana

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]

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
I dont think flatMap causes additional iteration, could you clarify?
H
Himank Batra

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]

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.
R
Raymond Chenon

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


Without performance benchmarks, this is questionable advice, especially since your argument about add does not refer to the bulk addAll operation.
O
Olivier Faucheux

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;
}

Don't forget the @SafeVarargs!
F
FDinoff

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);

How is the static import or the helper class relevant?
I think the method signature will be like - 'public static List join'. And 'return result'
c
cslysy

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());
}

m
martyglaubitz
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;
}

a
alex

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)));
}

L
Langusten Gustel
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;
  }

l
lqbweb

My favourite way, using fluent api and Guava:

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

2
2 revs, 2 users 92%

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(),
    ...
}))

why should someone never use those?
@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.
@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...
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
whenever you see usages of Vector, take a 5 minute break.
D
Daniel Rikowski

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);

s
shinzou

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());
}

J
Jan Weitz

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);
                  }}
              );

This would create a new annonymous class. Not recommended approach!