Come posso trasformare un elenco di elenchi in un elenco in Java 8?


534

Se ho un List<List<Object>>, come posso trasformarlo in un List<Object>che contiene tutti gli oggetti nello stesso ordine di iterazione usando le funzionalità di Java 8?

Risposte:


952

È possibile utilizzare flatMapper appiattire gli elenchi interni (dopo averli convertiti in stream) in un singolo stream, quindi raccogliere il risultato in un elenco:

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

11
@arshajii vero, ma per qualche ragione preferisco l'espressione lambda. Forse non mi piace l'aspetto di :::)
Eran,

25
Class::methodall'inizio sembra un po 'strano, ma ha il vantaggio di dichiarare da che tipo di oggetto stai mappando. Questo è qualcosa che altrimenti perdi nei flussi.
ArneHugo,

2
Tuttavia potresti voler avere un contenitore di un Elenco e in tal caso potresti dover usare lambda (l-> l.myList.stream ()).
Myoch,

2
Se è necessario eseguire il casting esplicito (ad esempio una matrice di primitive da elencare), potrebbe essere necessario anche lambda.
Michael Fulton,

52

flatmap è meglio ma ci sono altri modi per ottenere lo stesso

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

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

37

Il flatMapmetodo su Streampuò certamente appiattire quegli elenchi per te, ma deve creare Streamoggetti per elemento, quindi aStream per il risultato.

Non hai bisogno di tutti quegli Streamoggetti. Ecco il codice semplice e conciso per eseguire l'attività.

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

Perché a Listè Iterable, questo codice chiama il forEachmetodo (funzionalità Java 8), che è ereditato da Iterable.

Esegue l'azione specificata per ciascun elemento Iterablefino a quando tutti gli elementi non sono stati elaborati o l'azione genera un'eccezione. Le azioni vengono eseguite nell'ordine di iterazione, se tale ordine è specificato.

E un List's Iteratorrestituisce gli elementi in ordine sequenziale.

Per Consumerquesto, questo codice passa un riferimento di metodo (funzionalità Java 8) al metodo pre-Java 8 List.addAllper aggiungere in sequenza gli elementi dell'elenco interno.

Aggiunge tutti gli elementi nella raccolta specificata alla fine di questo elenco, nell'ordine in cui vengono restituiti dall'iteratore della raccolta specificata (operazione facoltativa).


3
Un buon suggerimento alternativo che evita alcune allocazioni non necessarie. Sarebbe interessante vedere il massimo utilizzo di questo quando si lavora con alcune raccolte più grandi, per vedere come si confrontano tra loro.
Per Lundberg,

12

È possibile utilizzare il flatCollect()modello da Collezioni Eclipse .

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

Se non puoi cambiare la lista da List:

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

Nota: sono un collaboratore delle raccolte Eclipse.


21
perché usare una dipendenza di terze parti quando la funzionalità è fornita da Java 8?
saw303,

2
L'API Eclipse Collections si trova sulla raccolta stessa, quindi il codice è conciso, è uno dei motivi principali in questo caso.
Nikhil Nanivadekar,

11

Proprio come ha detto @Saravana:

flatmap è migliore ma ci sono altri modi per ottenere lo stesso

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

Per riassumere, ci sono diversi modi per ottenere lo stesso risultato:

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

Voglio solo spiegare una situazione più simile List<Documents>, questa lista contiene alcune liste più di altri documenti come List<Excel>, List<Word>, List<PowerPoint>. Quindi la struttura è

class A {
  List<Documents> documentList;
}

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

Ora se vuoi iterare Excel solo dai documenti, fai qualcosa come sotto ...

Quindi il codice sarebbe

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

Spero che questo possa risolvere il problema di qualcuno durante la codifica ...


1

Possiamo usare flatmap per questo, si prega di fare riferimento al codice seguente:

 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

Un'espansione della risposta di Eran che è stata la risposta migliore, se hai un sacco di strati di liste, puoi continuare a formarle.

Questo viene anche fornito con un pratico modo di filtrare mentre scendi i livelli, se necessario.

Quindi per esempio:

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

Ciò sarebbe simile in SQL con le istruzioni SELECT all'interno delle istruzioni SELECT.


1

Metodo per convertire a List<List>in List:

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

Vedi questo esempio:

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

}       

Stampa:

listOfLists => [[a, b, c]]
list => [a, b, c]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.