リストのリストをJava 8のリストに変換するにはどうすればよいですか?


533

を持っている場合、Java 8の機能を使用して、同じ反復順序ですべてのオブジェクトを含むList<List<Object>>にどのように変換できList<Object>ますか?

回答:


951

を使用flatMapして、内部リストを(ストリームに変換した後)単一のストリームにフラット化し、結果をリストに収集します。

List<List<Object>> list = ...
List<Object> flat = 
    list.stream()
        .flatMap(List::stream)
        .collect(Collectors.toList());

11
@arshajiiは真実ですが、なんらかの理由でラムダ式を好みます。多分私は:::) の見た目が好きではありません
Eran

25
Class::method最初は少し変な感じがしますが、マッピング元のオブジェクトの種類を宣言するという利点があります。そうでなければ、ストリームで失うものです。
ArneHugo 2016年

2
しかし、リストのコンテナが必要な場合や、ラムダ(l-> l.myList.stream())を使用する必要がある場合があります。
Myoch

2
明示的なキャスト(たとえば、プリミティブの配列からリストへ)を行う必要がある場合は、ラムダも必要になる場合があります。
マイケルフルトン2017年

52

flatmap より良いですが、同じことを達成する他の方法があります

List<List<Object>> listOfList = ... // fill

List<Object> collect = 
      listOfList.stream()
                .collect(ArrayList::new, List::addAll, List::addAll);

37

flatMap上の方法Stream缶は確かにあなたのためのこれらのリストを平らにしますが、作成する必要がありStream、その後、要素のオブジェクトをStream結果のために。

これらすべてのStreamオブジェクトは必要ありません。これは、タスクを実行するためのシンプルで簡潔なコードです。

// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);

a ListIterableであるため、このコードはから継承されforEachメソッド(Java 8機能)を呼び出しますIterable

Iterableすべての要素が処理されるか、アクションが例外をスローするまで、の各要素に対して指定されたアクションを実行します。アクションは、その順序が指定されている場合、反復の順序で実行されます。

また、List' Iteratorはアイテムを順番に返します。

の場合Consumer、このコードはメソッド参照(Java 8機能)をJava 8より前のメソッドに渡してList.addAll、内部リスト要素を順番に追加します。

指定されたコレクションのすべての要素を、指定されたコレクションの反復子によって返される順序でこのリストの末尾に追加します(オプションの操作)。


3
いくつかの不要な割り当てを回避する優れた代替案。いくつかのより大きなコレクションを操作するときに、ヒープの最大使用量を確認し、それらを互いに比較する方法を確認すると興味深いでしょう。
Lundberg氏による

12

EclipseコレクションflatCollect()パターンを使用できます。

MutableList<List<Object>> list = Lists.mutable.empty();
MutableList<Object> flat = list.flatCollect(each -> each);

リストをから変更できない場合List

List<List<Object>> list = new ArrayList<>();
List<Object> flat = ListAdapter.adapt(list).flatCollect(each -> each);

注:私はEclipseコレクションの寄稿者です。


21
機能がJava 8によって提供されるときに、サードパーティの依存関係を使用する理由
saw303

2
EclipseコレクションAPIはコレクション自体にあるため、コードが簡潔であることが、この場合の主な理由の1つです。
Nikhil Nanivadekar 2017年

11

@サラバナが述べたように:

フラットマップの方が良いですが、同じことを達成する他の方法があります

 listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
 });

要約すると、次のように同じことを実現するいくつかの方法があります。

private <T> List<T> mergeOne(Stream<List<T>> listStream) {
    return listStream.flatMap(List::stream).collect(toList());
}

private <T> List<T> mergeTwo(Stream<List<T>> listStream) {
    List<T> result = new ArrayList<>();
    listStream.forEach(result::addAll);
    return result;
}

private <T> List<T> mergeThree(Stream<List<T>> listStream) {
    return listStream.reduce(new ArrayList<>(), (l1, l2) -> {
        l1.addAll(l2);
        return l1;
    });
}

private <T> List<T> mergeFour(Stream<List<T>> listStream) {
    return listStream.reduce((l1, l2) -> {
        List<T> l = new ArrayList<>(l1);
        l.addAll(l2);
        return l;
    }).orElse(new ArrayList<>());
}

private <T> List<T> mergeFive(Stream<List<T>> listStream) {
    return listStream.collect(ArrayList::new, List::addAll, List::addAll);
}

11

私はちょうど1つのより多くのシナリオのように説明したいList<Documents>、このリストのような他の文書のいくつかのより多くのリストが含まれていますList<Excel>List<Word>List<PowerPoint>。したがって、構造は

class A {
  List<Documents> documentList;
}

class Documents {
  List<Excel> excels;
  List<Word> words;
  List<PowerPoint> ppt;
}

ドキュメントからのみExcelを反復処理する場合は、次のようにします。

したがって、コードは

 List<Documents> documentList = new A().getDocumentList();

 //check documentList as not null

 Optional<Excel> excelOptional = documentList.stream()
                         .map(doc -> doc.getExcel())
                         .flatMap(List::stream).findFirst();
 if(excelOptional.isPresent()){
   Excel exl = optionalExcel.get();
   // now get the value what you want.
 }

これがコーディング中に誰かの問題を解決できることを願っています...


1

これにはフラットマップを使用できます。以下のコードを参照してください:

 List<Integer> i1= Arrays.asList(1, 2, 3, 4);
 List<Integer> i2= Arrays.asList(5, 6, 7, 8);

 List<List<Integer>> ii= Arrays.asList(i1, i2);
 System.out.println("List<List<Integer>>"+ii);
 List<Integer> flat=ii.stream().flatMap(l-> l.stream()).collect(Collectors.toList());
 System.out.println("Flattened to List<Integer>"+flat);

1

トップの回答であったエランの回答の拡張。リストの層がたくさんある場合は、それらをフラットマッピングし続けることができます。

これには、必要に応じてレイヤーを下に移動するときにも便利なフィルタリング方法が付属しています。

だから例えば:

List<List<List<List<List<List<Object>>>>>> multiLayeredList = ...

List<Object> objectList = multiLayeredList
    .stream()
    .flatmap(someList1 -> someList1
        .stream()
        .filter(...Optional...))
    .flatmap(someList2 -> someList2
        .stream()
        .filter(...Optional...))
    .flatmap(someList3 -> someList3
        .stream()
        .filter(...Optional...))
    ...
    .collect(Collectors.toList())

これは、SQLでSELECTステートメント内にSELECTステートメントを含めるのと似ています。


1

a List<List>をに変換する方法List

listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());

次の例をご覧ください。

public class Example {

    public static void main(String[] args) {
        List<List<String>> listOfLists = Collections.singletonList(Arrays.asList("a", "b", "v"));
        List<String> list = listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());

        System.out.println("listOfLists => " + listOfLists);
        System.out.println("list => " + list);
    }

}       

それは印刷します:

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