Jeśli mam List<List<Object>>, w jaki sposób mogę zmienić to w List<Object>zawierające wszystkie obiekty w tej samej kolejności iteracji, używając funkcji Java 8?
Jeśli mam List<List<Object>>, w jaki sposób mogę zmienić to w List<Object>zawierające wszystkie obiekty w tej samej kolejności iteracji, używając funkcji Java 8?
Odpowiedzi:
Możesz użyć, flatMapaby spłaszczyć wewnętrzne listy (po przekonwertowaniu ich na strumienie) w pojedynczy strumień, a następnie zebrać wynik w listę:
List<List<Object>> list = ...
List<Object> flat =
list.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
Class::methodna początku wydaje się trochę dziwny, ale ma tę zaletę, że deklaruje, z jakiego obiektu mapujesz. To jest coś, co inaczej tracisz w strumieniach.
Ta flatMapmetoda Streammoże z pewnością spłaszczyć te listy, ale musi tworzyć Streamobiekty dla elementu, a następnie Streamdla wyniku.
Nie potrzebujesz wszystkich tych Streamobiektów. Oto prosty, zwięzły kod do wykonania zadania.
// listOfLists is a List<List<Object>>.
List<Object> result = new ArrayList<>();
listOfLists.forEach(result::addAll);
Ponieważ Listjest Iterableto kod wywołuje się forEachmetodę (Java 8 funkcji), który jest dziedziczony Iterable.
Wykonuje podaną akcję dla każdego elementu,
Iterabledopóki wszystkie elementy nie zostaną przetworzone lub akcja wygeneruje wyjątek. Akcje są wykonywane w kolejności iteracji, jeśli ta kolejność jest określona.
A List„s Iteratorelementy powraca w kolejności.
W Consumertym przypadku kod przekazuje odwołanie do metody (funkcja Java 8) do metody wcześniejszej niż Java 8, List.addAllaby sekwencyjnie dodawać wewnętrzne elementy listy.
Dołącza wszystkie elementy w określonej kolekcji na końcu tej listy, w kolejności, w jakiej są zwracane przez iterator określonej kolekcji (operacja opcjonalna).
Możesz użyć flatCollect()wzorca z kolekcji Eclipse .
MutableList<List<Object>> list = Lists.mutable.empty();
MutableList<Object> flat = list.flatCollect(each -> each);
Jeśli nie możesz zmienić listy z List:
List<List<Object>> list = new ArrayList<>();
List<Object> flat = ListAdapter.adapt(list).flatCollect(each -> each);
Uwaga: Jestem współtwórcą kolekcji Eclipse.
Tak jak wspomniano @ Saravana:
flatmap jest lepszy, ale istnieją inne sposoby osiągnięcia tego samego
listStream.reduce(new ArrayList<>(), (l1, l2) -> {
l1.addAll(l2);
return l1;
});
Podsumowując, istnieje kilka sposobów osiągnięcia tego samego, co następuje:
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);
}
Chcę tylko wyjaśnić jeszcze jeden scenariusz jak List<Documents>ta lista zawiera kilka dodatkowych wykazów innych dokumentów, takich jak List<Excel>, List<Word>, List<PowerPoint>. Więc struktura jest
class A {
List<Documents> documentList;
}
class Documents {
List<Excel> excels;
List<Word> words;
List<PowerPoint> ppt;
}
Teraz, jeśli chcesz iterować Excela tylko z dokumentów, zrób coś takiego jak poniżej.
Tak więc kod będzie
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.
}
Mam nadzieję, że to rozwiąże czyjś problem podczas kodowania ...
Możemy użyć do tego płaskiej mapy, zapoznaj się z poniższym kodem:
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);
Rozszerzenie odpowiedzi Erana, które było najlepszą odpowiedzią, jeśli masz kilka warstw list, możesz je nadal płasko odwzorowywać.
Jest to również przydatny sposób filtrowania podczas schodzenia po warstwach, jeśli to konieczne.
Na przykład:
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())
W SQL byłoby to podobne do posiadania instrukcji SELECT w instrukcjach SELECT.
Metoda konwersji List<List>na List:
listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
Zobacz ten przykład:
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);
}
}
Drukuje:
listOfLists => [[a, b, c]]
list => [a, b, c]
:::)