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ć, flatMap
aby 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::method
na 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 flatMap
metoda Stream
może z pewnością spłaszczyć te listy, ale musi tworzyć Stream
obiekty dla elementu, a następnie Stream
dla wyniku.
Nie potrzebujesz wszystkich tych Stream
obiektó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ż List
jest Iterable
to kod wywołuje się forEach
metodę (Java 8 funkcji), który jest dziedziczony Iterable
.
Wykonuje podaną akcję dla każdego elementu,
Iterable
dopó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 Iterator
elementy powraca w kolejności.
W Consumer
tym przypadku kod przekazuje odwołanie do metody (funkcja Java 8) do metody wcześniejszej niż Java 8, List.addAll
aby 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]
::
:)