I bawił się z roztworu za pomocą groupingBy
, mapping
i reducing
na następujące pytanie: Elegantly create map with object fields as key/value from object stream in Java 8. Podsumowując, celem było uzyskanie mapy z wiekiem jako kluczem i hobby osoby jako Set
.Java 8 stream.collect (... groupingBy (... mapping (... zmniejszenie))) zmniejszenie BinaryOperator-usage
Jedno z rozwiązań, które wymyśliłem (niezbyt fajne, ale nie o to chodzi) miało dziwne zachowanie.
Z poniższej listy jako wejście:
List<Person> personList = Arrays.asList(
new Person(/* name */ "A", /* age */ 23, /* hobbies */ asList("a")),
new Person("BC", 24, asList("b", "c")),
new Person("D", 23, asList("d")),
new Person("E", 23, asList("e"))
);
i następujące rozwiązanie:
Collector<List<String>, ?, Set<String>> listToSetReducer = Collectors.reducing(new HashSet<>(), HashSet::new, (strings, strings2) -> {
strings.addAll(strings2);
return strings;
});
Map<Integer, Set<String>> map = personList.stream()
.collect(Collectors.groupingBy(o -> o.age,
Collectors.mapping(o -> o.hobbies, listToSetReducer)));
System.out.println("map = " + map);
mam:
map = {23=[a, b, c, d, e], 24=[a, b, c, d, e]}
wyraźnie nie to, czego się spodziewałem. I raczej spodziewać to:
map = {23=[a, d, e], 24=[b, c]}
Teraz, jeśli po prostu zastąpić kolejność (strings, strings2)
operatorem binarnym (kolektora redukującego) (strings2, strings)
mogę uzyskać oczekiwany rezultat. Więc, za czym tęskniłem? Czy źle interpretowałem reducing
-lekser? A może brakowało mi dokumentacji, która sprawiała, że moje użycie nie działało zgodnie z oczekiwaniami?
Wersja Java to 1.8.0_121, jeśli to ma znaczenie.
Ciekawe .. Początkowo próbowałem nawet do stwierdzenia, że 'flatMapping' w' Collectors' (lub zastosować 'hobbies.stream()' i pracować z nim) . Dobrze wiedzieć, że to nadchodzi. Och, drogi, pomieszałem kombinator z operatorem. To był tylko zbieg okoliczności, że zmiana parametrów przyniosła właściwy rezultat. Dzięki za wyjaśnienie! – Roland
Tak, w kontekście sekwencyjnym funkcja redukcji będzie zawsze oceniana jako 'f (poprzednia, następna)' podczas gdy 'previous' będzie wartością tożsamości dla pierwszej oceny i poprzednim wynikiem dla kolejnych ocen. Tak więc '(a, b) -> a' zawsze kończy się wartością tożsamości, podczas gdy' (a, b) -> b' używa świeżego zestawu stworzonego przez funkcję mappera. Ale w równoległej ocenie oba argumenty mogą być wynikiem wcześniejszej częściowej oceny, a ponieważ częściowe wyniki mogą być puste, albo argument może być wartością tożsamości, więc użycie drugiego argumentu nie jest niezawodnym rozwiązaniem. – Holger