Wie kann ich eine Liste von Listen in eine Liste in Java 8 verwandeln?


532

Wenn ich eine habe List<List<Object>>, wie kann ich daraus eine machen List<Object>, die alle Objekte in derselben Iterationsreihenfolge enthält, indem ich die Funktionen von Java 8 verwende?

Antworten:


950

Sie können flatMapdie internen Listen (nachdem Sie sie in Streams konvertiert haben) in einen einzelnen Stream reduzieren und das Ergebnis dann in einer Liste zusammenfassen:

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

11
@arshajii stimmt, aber aus irgendeinem Grund bevorzuge ich den Lambda-Ausdruck. Vielleicht mag ich das Aussehen von :::)
Eran

25
Class::methodfühlt sich anfangs etwas komisch an, hat aber den Vorteil, dass es deklariert, von welcher Art von Objekt Sie ein Mapping durchführen. Das ist etwas, was Sie sonst in Streams verlieren.
ArneHugo

2
Möglicherweise möchten Sie jedoch einen Container mit einer Liste haben. In diesem Fall müssen Sie möglicherweise das Lambda (l-> l.myList.stream ()) verwenden.
Myoch

2
Wenn Sie explizites Casting durchführen müssen (z. B. eine Reihe von Grundelementen für List), sind möglicherweise auch Lambdas erforderlich.
Michael Fulton

52

flatmap ist besser, aber es gibt andere Möglichkeiten, das gleiche zu erreichen

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

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

37

Die flatMapMethode on Streamkann diese Listen sicherlich für Sie reduzieren, muss jedoch StreamObjekte für das Element und dann a Streamfür das Ergebnis erstellen .

Sie brauchen nicht alle diese StreamObjekte. Hier ist der einfache, präzise Code zur Ausführung der Aufgabe.

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

Da a Listis ist Iterable, ruft dieser Code die forEachMethode (Java 8-Funktion) auf, von der geerbt wird Iterable.

Führt die angegebene Aktion für jedes Element von aus, Iterablebis alle Elemente verarbeitet wurden oder die Aktion eine Ausnahme auslöst. Aktionen werden in der Reihenfolge der Iteration ausgeführt, wenn diese Reihenfolge angegeben ist.

Und a List's Iteratorgibt Artikel in sequentieller Reihenfolge zurück.

Für den ConsumerCode wird eine Methodenreferenz (Java 8-Funktion) an die Methode vor Java 8 übergeben List.addAll, um die inneren Listenelemente nacheinander hinzuzufügen.

Hängt alle Elemente in der angegebenen Sammlung an das Ende dieser Liste in der Reihenfolge an, in der sie vom Iterator der angegebenen Sammlung zurückgegeben werden (optionale Operation).


3
Guter alternativer Vorschlag, der unnötige Zuweisungen vermeidet. Es wäre interessant, die Top-Heap-Nutzung zu sehen, wenn Sie mit einigen größeren Sammlungen arbeiten, um zu sehen, wie sie miteinander verglichen werden.
Per Lundberg

12

Sie können das flatCollect()Muster aus Eclipse-Sammlungen verwenden .

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

Wenn Sie die Liste nicht ändern können von List:

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

Hinweis: Ich bin ein Mitarbeiter von Eclipse Collections.


21
Warum sollte eine Abhängigkeit von Drittanbietern verwendet werden, wenn die Funktionalität von Java 8 bereitgestellt wird?
saw303

2
Die Eclipse Collections-API befindet sich in der Sammlung selbst, daher ist der Code präzise. Dies ist einer der Hauptgründe in diesem Fall.
Nikhil Nanivadekar

11

Genau wie @Saravana erwähnt hat:

Flatmap ist besser, aber es gibt andere Möglichkeiten, dasselbe zu erreichen

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

Zusammenfassend gibt es verschiedene Möglichkeiten, um dasselbe zu erreichen:

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

Ich möchte nur ein weiteres Szenario wie erklären List<Documents>, diese Liste enthält einige weitere Listen anderer Dokumente wie List<Excel>, List<Word>, List<PowerPoint>. Die Struktur ist also

class A {
  List<Documents> documentList;
}

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

Wenn Sie Excel nur aus Dokumenten iterieren möchten, gehen Sie wie folgt vor.

Der Code wäre also

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

Ich hoffe, dies kann jemandes Problem beim Codieren lösen ...


1

Wir können hierfür Flatmap verwenden, siehe folgenden Code:

 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

Eine Erweiterung von Erans Antwort, die die beste Antwort war. Wenn Sie mehrere Listenebenen haben, können Sie diese weiterhin flach abbilden.

Dies bietet auch eine praktische Möglichkeit zum Filtern, wenn Sie bei Bedarf auch die Ebenen herunterfahren.

Also zum Beispiel:

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

Dies ähnelt in SQL SELECT-Anweisungen in SELECT-Anweisungen.


1

Methode zum Konvertieren von a List<List>in List:

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

Siehe dieses Beispiel:

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

}       

Es druckt:

listOfLists => [[a, b, c]]
list => [a, b, c]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.