¿Agrupación de Java 8 con un recopilador personalizado?
lambda java-8 (3)
Tengo la siguiente clase.
class Person {
String name;
LocalDate birthday;
Sex gender;
String emailAddress;
public int getAge() {
return birthday.until(IsoChronology.INSTANCE.dateNow()).getYears();
}
public String getName() {
return name;
}
}
Me gustaría poder agrupar por edad y luego recopilar la lista de nombres de personas en lugar del objeto Persona en sí; todo en una sola expresión agradable de lamba.
Para simplificar todo esto, estoy vinculando mi solución actual que almacena el resultado de la agrupación por edad y luego lo repite para recopilar los nombres.
ArrayList<OtherPerson> members = new ArrayList<>();
members.add(new OtherPerson("Fred", IsoChronology.INSTANCE.date(1980, 6, 20), OtherPerson.Sex.MALE, "[email protected]"));
members.add(new OtherPerson("Jane", IsoChronology.INSTANCE.date(1990, 7, 15), OtherPerson.Sex.FEMALE, "[email protected]"));
members.add(new OtherPerson("Mark", IsoChronology.INSTANCE.date(1990, 7, 15), OtherPerson.Sex.MALE, "[email protected]"));
members.add(new OtherPerson("George", IsoChronology.INSTANCE.date(1991, 8, 13), OtherPerson.Sex.MALE, "[email protected]"));
members.add(new OtherPerson("Bob", IsoChronology.INSTANCE.date(2000, 9, 12), OtherPerson.Sex.MALE, "[email protected]"));
Map<Integer, List<Person>> collect = members.stream().collect(groupingBy(Person::getAge));
Map<Integer, List<String>> result = new HashMap<>();
collect.keySet().forEach(key -> {
result.put(key, collect.get(key).stream().map(Person::getName).collect(toList()));
});
No es ideal y, en aras del aprendizaje, me gustaría tener una solución más elegante y eficiente.
Al agrupar una secuencia con
Collectors.groupingBy
, puede especificar una operación de reducción en los valores con un
Collector
personalizado.
Aquí, necesitamos usar
Collectors.mapping
, que toma una función (qué es el mapeo) y un colector (cómo recolectar los valores mapeados).
En este caso, la asignación es
Person::getName
, es decir, una referencia de método que devuelve el nombre de la Persona, y la recopilamos en una
List
.
Map<Integer, List<String>> collect =
members.stream()
.collect(Collectors.groupingBy(
Person::getAge,
Collectors.mapping(Person::getName, Collectors.toList()))
);
Puede usar un
Collector
mapping
para asignar la lista de
Person
a una lista de nombres de personas:
Map<Integer, List<String>> collect =
members.stream()
.collect(Collectors.groupingBy(Person::getAge,
Collectors.mapping(Person::getName, Collectors.toList())));
También puede usar Collectors.toMap y proporcionar una asignación para la función de clave, valor y combinación (si corresponde).
Map<Integer, String> ageNameMap =
members.stream()
.collect(Collectors.toMap(
person -> person.getAge(),
person -> person.getName(), (pName1, pName2) -> pName1+"|"+pName2)
);