metodos - stream java 8 ejemplo
Agrupar por un atributo de Colección utilizando Java Streams (2)
Tengo un objeto que contiene una colección de cadenas, digamos los idiomas que habla una persona.
public class Person {
private String name;
private int age;
private List<String> languagesSpoken;
// ...
}
Ahora, creando algunas instancias como esta ...
Person p1 = new Person("Bob", 21, Arrays.asList("English", "French", "German"));
Person p2 = new Person("Alice", 33, Arrays.asList("English", "Chinese", "Spanish"));
Person p3 = new Person("Joe", 43, Arrays.asList("English", "Dutch", "Spanish", "German"));
//put them in list
List<Person> people = Arrays.asList(p1,p2,p3);
... lo que quiero es un Map<String, List<Person>>
, para cada idioma que incluya a las personas que hablan el idioma:
["English" -> [p1, p2, p3],
"German" -> [p1, p3],
etc. ]
Por supuesto, esto se puede programar fácilmente de manera imperativa, pero ¿cómo hacerlo de manera funcional con Java Streams? He intentado algo como people.stream.collect(groupingBy(Person::getLanguagesSpoken))
pero eso, por supuesto, me da un Map<List<String>, List<Person>>
. Todos los ejemplos que pude encontrar están usando groupingBy
on Primitives o Strings.
Esto también es posible hacerlo sin transmisiones, aún utilizando las nuevas características de java-8.
people.forEach(x -> {
x.getLanguagesSpoken().forEach(lang -> {
langPersons.computeIfAbsent(lang, ignoreMe -> new ArrayList<>()).add(x);
});
});
Puede dividir las instancias de Person
en pares de lenguaje y Person
(usando flatMap
) y luego agruparlos según sea necesario:
Map<String, List<Person>> langPersons =
people.stream()
.flatMap(p -> p.getLanguagesSpoken()
.stream()
.map(l -> new SimpleEntry<>(l,p)))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));