Próbuję zmienić jakiś niezbyt elegancki kod za pomocą strumienia. Mam HashMap zawierających ciągi i MyObjects a obecnie iteracji nad nim za pomocą pętli for tak:Jak mogę wykonać dwie różne funkcje w kolekcji na podstawie grupowania ze strumieniem?
Map<String, MyObject> map = new HashMap<>();
Map<String, MyObject> objectsToAdd = new HashMap<>();
for(MyObject object : map.values()){
String idToAdd = object.getConnectedToId();
if(StringUtils.isEmpty(idToAdd) {
continue;
}
if(idToAdd.substring(0,1).equals("i")){ // connected to an ICS
MyObject newObject = service1.someMethod(idToAdd);
if(newObject != null) {
objectsToAdd.put(newObject.getId(), newObject);
}
} else if (idToAdd.substring(0,1).equals("d")){ // connected to a device
MyObject newObject = service2.someMethod(idToAdd);
if(newObject != null) {
objectsToAdd.put(newObject.getId(), newObject);
}
}
}
map.putAll(objectsToAdd);
Odkąd tylko dbają o identyfikatory, zacząłem za pomocą operacji mapie, aby uzyskać jedynie identyfikatory, a następnie operacja filtrowania w celu wyeliminowania pustych.
Następna część jest tym, z czym mam problem. Pierwszą rzeczą, jaką starał się za pomocą kolektorów operacji groupingBy tak, że mogę grupować elementy w oparciu o pierwszy znak identyfikatora i skończyło się tak:
map.values().stream()
.map(myObject -> myObject.getConnectedToId()) // get a map of all the ids
.filter(StringUtils::isNotEmpty) // filter non empty ones
.collect(
Collectors.mapping(
MyObject::getId,
Collectors.toList())),
Collectors.groupingBy(
s -> s.substring(0,1));
Ten link pomógł z redukcją z zastosowaniem kolektorów Stream: Stream Reduction
Mamy co najmniej dwa problemy z tym kodem: 1) collect to terminal operation, który zamknie strumień, a my jeszcze nie skończyliśmy, i 2) wciąż potrzebujemy oryginalnego obiektu, ale teraz został zredukowany do mapa connectToIds.
Q1) Czy istnieje pośrednia operacja, która pozwoli nam pogrupować obiekty na podstawie pierwszego znaku identyfikatora?
Q2) Jak możemy to zrobić, nie ograniczając kolekcji tylko do identyfikatorów?
Q3) I na koniec, gdy kolekcja zostanie zgrupowana (będą dwie), w jaki sposób możemy wykonać oddzielne funkcje w każdej grupie, tak jak w oryginalnym kodzie?
Ostateczne rozwiązanie (dzięki @Holger & @Flown za pomoc)
Map<Character, Function<String, MyObejct>> methodMapping = new HashMap<>();
methodMapping.put('i', service1::method1);
methodMapping.put('d', service2::method2);
Map<String, MyObject> toAdd = map.values().stream().map(MyObject::getConnectedToId)
.filter(StringUtils::isNotEmpty)
.map(id -> methodMapping.getOrDefault(id.charAt(0), i -> null).apply(id))
.filter(Objects::nonNull)
.collect(Collectors.toMap(MyObject::getId, Function.identity(), (mo1, mo2) -> mo2));
map.putAll(toAdd);
Aby uniknąć modyfikacji współbieżne wyjątku, konieczne jest najpierw zapisać obiekty w tymczasowym mapie podczas wykonywania operacje strumieniowe, a następnie po zakończeniu dodawania ich do ostatecznej mapy.
Zakładam, że kluczem mapy jest 'id' z' MyObject'? To znaczy. 'map.get (object.getId()). equals (object) == true' – Flown
Tak, klucz w HashMap jest identyfikatorem MyObject. Wprowadziłem małą poprawkę do pytania dla jasności - idToAdd to connectToId na myObject, a nie identyfikator obiektu. – Kristina