Javaで2つのリストを結合するにはどうすればよいですか?


749

条件:元のリストを変更しないでください。JDKのみ、外部ライブラリなし。ワンライナーまたはJDK 1.3バージョンのボーナスポイント。

より簡単な方法はありますか:

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

5
あなたはこれをやっている場合は、反復の目的は、別の質問を見るためだけに-グアバとJava 8ソリューションのgoogleがあるstackoverflow.com/questions/4896662/...
ボリスTreukhov

ユーティリティメソッドを使用したJava 8ソリューション:stackoverflow.com/a/37386846/1216775
akhil_mittal

いくつかの回答を読んだ後、申し訳ありませんでした。
Anthony Rutledge

回答:


591

Java 8の場合:

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

82
ああ、Java 8のことなのか?技術的には勝つと思いますが、それは長い列のまとめです:-)
Robert Atkins

4
:カジュアルな読者のために、ここにもJavaを使用して短いソリューションは、Streams _ですstackoverflow.com/a/34090554/363573
ステファン・

7
醜いですが、少なくとも流暢で、マルチラインラムダなしで使用できます。連結リストを返す流暢なaddAllがあったら本当によかったです。
Usman Ismail

6
:私はそれの価値は、それはそうのように、あまりにもこれの明確なリストを取得するには本当に簡単だことは注目に推測List<String> newList = Stream.concat(listOne.stream(), listTwo.stream()).distinct().collect(Collectors.toList());
ロジャー・

1
連結の代替:ストリームのストリームStream.of(listOne, listTwo).flatMap(Collection::stream).collect(Collectors.toList())
Peter Walser

569

頭の上から1行短くできます。

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

156
技術的には正しいのですが、1行短くしましたが、この非対称性は私にバグをもたらします。余計な行を「費やす」ことで幸せになるほどで​​す。
Robert Atkins、2011

13
newListの内部配列がlistOneのサイズに初期化され、listTwoからすべての項目を追加するときに拡張する必要がある可能性があるという問題はありませんか?各リストのサイズを取得し、それを使用して新しい配列のサイズを設定する方が良いでしょうか?
Eric

2
これが私にとって最も効果的な解決策でした。空のリストを作成し、さらにそのaddAll()両方で、これが勝者となったさまざまなソリューションのパフォーマンスを比較しました。リストをコピーしないことを提案するものをすべて試したところ、今回は不要なオーバーヘッドが多く発生しました。
manuelvigarcia

いいですが、短くすることもできます。List list = new ArrayList <>(list1).addAll(list2);
速度

1
@速度:いいえ、それはうまくいきません。addAll(Collection)を返しますboolean
Stijn Van Bael、

306

Apache commons-collectionsライブラリを使用できます。

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

52
いいですが、Apache Commonsが必要です。彼は「外部ライブラリなし」を指定しました
Quantum7

101
@ Quantum7、まだ他の人にとっても便利です;)また、Apache Commonsは外部ライブラリでもありますか?それなしでは何も始めません!
tster 2012年

28
@Platinumいいえ、ドキュメントによると、ListUtils.unionはOPのコードとまったく同じです。しかし、リストコンテキストでSET操作( "Union")を使用するのは誤解を招く可能性があります。これで重複などが削除されることを期待する方法を私は見ることができますが、メソッドはそれを行わないようです。
Quantum7

24
Apache Commons Collectionは避けてください。タイプセーフではなく、ジェネリックはありません。Java 1.4を使用している場合は素晴らしいですが、Java 5以降の場合は、Google Guavaを使用します。
Michael Piefel 2013

11
@MichaelPiefel最新のApache Commons Collections 4はタイプセーフです。Java 8メソッドリファレンスでは、この種の静的ユーティリティが非常に重要になります。
mingfai 2014年

93

要件の1つは、元のリストを保持することです。新しいリストを作成してを使用するとaddAll()、リスト内のオブジェクトへの参照数が事実上2倍になります。リストが非常に大きい場合、これはメモリの問題につながる可能性があります。

連結した結果を変更する必要がない場合は、カスタムリストの実装を使用してこれを回避できます。カスタム実装クラスは明らかに複数行ですが、それを使用することは短くて便利です。

CompositeUnmodifiableList.java:

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

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

    public CompositeUnmodifiableList(List<E> list1, List<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();
    }
}

使用法:

List<String> newList = new CompositeUnmodifiableList<String>(listOne,listTwo);

15
これがこの質問に対する本当の答えです。
Wouter Lievens 2013年

9
これは実行可能なソリューションですが、基になるリストオブジェクト(list1、list2)が変更されると、このリストの内容が変更されることに注意してください。CompositeUnmodifiableList 自体のインスタンスを変更できない場合がありますが、元のリストへの参照を取得できれば可能です。また、馴染みのない人のために:final修飾子は単にリストオブジェクトへの参照に影響を与えるだけで変更はできませんが、リストの内容が変更される可能性があります!
jwj 14

3
@jwjすべての非常に良い点、ありがとうございます。クラス名はおそらく説明に値します。このクラスはCollections.unmodifiableList()、リストをラップして変更できないようにするメソッドと非常によく似た動作をしていると思います。 CompositeUnmodifiableList2つのリストをラップし、連結ビューを提供することを除いて、同じことを行います。あなたがするすべてのポイントCompositeUnmodifiableListも同様に当てはまりCollections.unmodifiableList()ます。
Kevin K

2
コンストラクターが取ることができるList<? extends E>
Patrick Parker

84

おそらく単純ではありませんが、興味深く、醜いです:

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

量産コードでは使用しないでください...;)


44
醜くて邪悪で、ダブルブレースの初期化とほぼ同じです。しかし、それはより短いです;)
Jorn

4
@MarnixKlooster:Eclipse はそれを使用してはならないことを知っているため、使用するのが不快になります;-)
Joachim Sauer

20
物理的には1行ですが、「1行」とは思いません。
splungebob

11
なぜ人々は匿名のブロック初期化子を嫌うのですか
NimChimpsky

18
@NimChimpskyこれは主に匿名ブロック初期化子ではないためだと思いますが、実際にはArrayListの匿名サブクラスを作成しています。とはいえ、このダブルブレースの初期化に関する質問の結果を信頼すると、DBIを嫌うのは主にスタイルの好みとマイクロ最適化の問題のように思えます。私の知る限り、それを行うことに対する大きなペナルティはありません。卑劣な欠点は、ArrayListにならないためにクラスを比較しようとした場合です。
Patrick

75

別のJava 8ワンライナー:

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

ボーナスとして、Stream.of()は可変なので、好きなだけリストを連結できます。

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

35
x -> x.stream()で置き換えることができますCollection::stream
マーティン

10
...またはList::stream
MC皇帝

73

単純ではありませんが、オーバーヘッドのサイズ変更はありません。

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

55

外部ライブラリを気にせず、任意の量のリストを連結することを目的としたこの質問が見つかりました。だから、おそらくそれは誰かを助けるでしょう:

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

同じロジックを1つのfor()の複数の異なるコレクションに適用する場合に便利です。


9
例:Lists.newArrayList(Iterables.concat(list1、list2));
meilechh

com.google.common.collect.Iterators#concat(java.util.Iterator<? extends java.util.Iterator<? extends T>>)代わりに呼び出す必要がありIterables#concat()ます。後者はまだ要素を一時リンクにコピーするためです!
bob

45

Java 8(Stream.ofおよびStream.concat

提案されたソリューションは3つのリスト用ですが、2つのリストにも適用できます。Java 8では、次のようにStream.ofまたはStream.concatを使用できます。

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.concat2つのストリームを入力として取り、要素が最初のストリームのすべての要素であり、その後に2番目のストリームのすべての要素が続く遅延連結ストリームを作成します。3つのリストがあるため、この方法(Stream.concat)を2回使用しました。

また、任意の数のリスト(varargsを使用)を取り、連結リストを次のように返すメソッドでユーティリティクラスを作成することもできます。

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

次に、このメソッドを次のように使用できます。

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

あなたはおそらくList <String>を言っても構わないと思います。最初のオペレーターで。修正してください。
WebComer 2017

44

2行を使用したjava 8ソリューションを次に示します。

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

この方法は次の場合には使用しないでください。

  • の出所newListは不明であり、すでに他のスレッドと共有されている可能性があります
  • 変更するストリームnewListは並列ストリームであり、アクセスnewListは同期され ていないか、スレッドセーフではありません

副作用の考慮のため。

上記の両方の条件は、2つのリストを結合する上記の場合には適用されないため、安全です。

別の質問に対するこの回答に基づく。


12
私が間違っていない場合、これは実際にはお勧めできません-docs.oracle.com/javase/8/docs/api/java/util/stream/…副作用セクションを参照してください。>ストリーム操作に対する動作パラメーターの副作用は、一般に推奨されません。これらは、無意識の要件の意図しない違反や、その他のスレッドセーフティの危険につながることが多いためです。したがって、この場合は、Collectors.toList()を使用することをお
勧めします

@AntonBalaniuc質問は、これが本当に副作用かどうかです。その時点でnewListは、他のスレッドからは監視できません。しかし、あなたはそれがの値が不明である場合、これはおそらく行われるべきではないと正しいnewList(例えば場合から来newListパラメータとして渡されました。
SpaceTrucker

2
私は興味がある; なぜ.forEach(newList::addAll);代わりに.collect(Collectors.toList());
11684

4
@ 11684コレクターがList<List<Object>>。:あなたが考えていることは、このようなものであるstackoverflow.com/questions/189559/...
SpaceTrucker

@SpaceTruckerおっと、見落としました。混乱を解消してくれてありがとう。はい、考えていたはずですflatMap
11684

34

これは単純で1行だけですが、listTwoの内容をlistOneに追加します。本当に3番目のリストにコンテンツを入れる必要がありますか?

Collections.addAll(listOne, listTwo.toArray());

11
元のリストを変更しないことは基準の1つでしたが、これが制約ではない状況の例としてここに置くと便利です。
Robert Atkins

1
ありがとう、またはもっと簡単なlistOne.addAll(listTwo)
Jay

27

少しシンプル:

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

これにより文字列が重複しますか?両方のリストに存在する文字列が結果のリストに2回存在することを意味しますか?
AgentKnopf

4
@Zainodisはい、重複する可能性があります。List構造には一意性制約を課しません。セットで同じことを行うと、重複を削除できます。Set<String> newSet = new HashSet<>(setOne); newSet.addAll(setTwo);
Patrick

20

少し短くなります:

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

17

汎用Java 8ユーティリティメソッドを作成して、任意の数のリストを連結できます

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

13

ターゲットリストが事前宣言されている場合は、ワンライナーを実行できます。

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


9

Java8ストリームを使用したもう1つのライナーソリューション。flatMapソリューションはすでに投稿されているため、ここにflatMap

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

または

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

コード

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

出力

[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]

1
flatMapリストが収集されるときに一度だけ
繰り返さ

7

私の意見で最も賢い:

/**
 * @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;
}

3
をお忘れなく@SafeVarargs
Radon Rosborough、2014

6

あなたは静的インポートとヘルパークラスでそれを行うことができます

NBこのクラスの総称は、おそらく改善することができました

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

}

その後、次のようなことができます

import static Lists.join;
List<T> result = join(list1, list2, list3, list4);

静的インポートまたはヘルパークラスはどのように関連していますか?
shmosel

6

オブジェクトキーによる結合をサポートするJava 8バージョン:

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

ヘルパークラスを使用します。

私は提案します:

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

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

2

2つの方法でjava8を使用して2つのリストを結合できます。

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

1)concatを使用する:

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

2)flatMapの使用:

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

1
他の30の回答で11年前の質問に回答するときは、回答の対象となる質問の新しい側面を指摘し、質問が行われたときにこれらの手法が機能したかどうか、またはそれらが以前からある機能に依存しているかどうかに注意してください長年にわたって導入されました。
Jason Aller

2

ほとんどの回答は、ArrayListの使用を提案しています。

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

効率的な追加操作には、LinkedListを使用することをお勧めします。

ArrayList addは償却されたO(1)ですが、配列のサイズを変更してコピーする必要があるため、O(n)は最悪のケースです。LinkedList addは常に定数O(1)です。

詳細情報https://stackoverflow.com/a/322742/311420


0

単純だとは言っていませんが、ワンライナーのボーナスについてお話しました;-)

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

なぜ誰かがそれらを決して使用すべきではないのですか?
David

5
@Davidは、JDKの内部での使用を目的としているためです。コードでそれを使用した場合、コードはおそらくSun以外(または現在Oracle以外)のJDK / JREで実行されません。
エイドリアンシャム2013

@AdrianShum Oracle以外のJDK / JREはありますか?それは私を驚かせます。最も一般的なAPI機能に制限されている場合でも、その全体を再構築するにはおそらく時間がかかります...
Egor Hans

1
JVMはかなりたくさんあります。エンタープライズの世界で最も一般的に見られるものは、Websphereにバンドルされているiirc、IBMのものである必要があります
Adrian Shum

0

ワンライナーに近い方法はありませんが、これが最も簡単だと思います。

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

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

0

リストに異なるタイプがあり、それらを別のタイプのリストに結合したい場合は、ストリームとJava 8を使用するアプローチを次に示します。

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

これを静的に実行したい場合は、以下を実行できます。

例では、2つのEnumSetを自然順(== Enum-order)A, Bで使用し、ALLリストに結合します。

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

これにより、新しい匿名クラスが作成されます。推奨されないアプローチ!
クラベミール

-3
import java.util.AbstractList;
import java.util.List;


/**
 * The {@code ConcatList} is a lightweight view of two {@code List}s.
 * <p>
 * This implementation is <em>not</em> thread-safe even though the underlying lists can be.
 * 
 * @param <E>
 *            the type of elements in this list
 */
public class ConcatList<E> extends AbstractList<E> {

    /** The first underlying list. */
    private final List<E> list1;
    /** The second underlying list. */
    private final List<E> list2;

    /**
     * Constructs a new {@code ConcatList} from the given two lists.
     * 
     * @param list1
     *            the first list
     * @param list2
     *            the second list
     */
    public ConcatList(final List<E> list1, final List<E> list2) {
        this.list1 = list1;
        this.list2 = list2;
    }

    @Override
    public E get(final int index) {
        return getList(index).get(getListIndex(index));
    }

    @Override
    public E set(final int index, final E element) {
        return getList(index).set(getListIndex(index), element);
    }

    @Override
    public void add(final int index, final E element) {
        getList(index).add(getListIndex(index), element);
    }

    @Override
    public E remove(final int index) {
        return getList(index).remove(getListIndex(index));
    }

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

    @Override
    public boolean contains(final Object o) {
        return list1.contains(o) || list2.contains(o);
    }

    @Override
    public void clear() {
        list1.clear();
        list2.clear();
    }

    /**
     * Returns the index within the corresponding list related to the given index.
     * 
     * @param index
     *            the index in this list
     * 
     * @return the index of the underlying list
     */
    private int getListIndex(final int index) {
        final int size1 = list1.size();
        return index >= size1 ? index - size1 : index;
    }

    /**
     * Returns the list that corresponds to the given index.
     * 
     * @param index
     *            the index in this list
     * 
     * @return the underlying list that corresponds to that index
     */
    private List<E> getList(final int index) {
        return index >= list1.size() ? list2 : list1;
    }

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.