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?
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:
È possibile utilizzare flatMap
per 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());
Class::method
all'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.
Il flatMap
metodo su Stream
può certamente appiattire quegli elenchi per te, ma deve creare Stream
oggetti per elemento, quindi aStream
per il risultato.
Non hai bisogno di tutti quegli Stream
oggetti. 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 forEach
metodo (funzionalità Java 8), che è ereditato da Iterable
.
Esegue l'azione specificata per ciascun elemento
Iterable
fino 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 Iterator
restituisce gli elementi in ordine sequenziale.
Per Consumer
questo, questo codice passa un riferimento di metodo (funzionalità Java 8) al metodo pre-Java 8 List.addAll
per 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).
È 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.
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);
}
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 ...
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);
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.
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]
::
:)