使用 Java 流按集合属性分组

2022-09-01 19:09:43

我有一个对象,其中包含字符串的集合,假设一个人说的语言。

public class Person {
   private String name;
   private int age;
   private List<String> languagesSpoken;

   // ...
}

现在,创建一些这样的实例...

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);

...我想要的是,对于列出说该语言的人的每种语言::Map<String, List<Person>>

["English" -> [p1, p2, p3],
 "German"  -> [p1, p3],
 etc. ]

当然,这可以很容易地以命令式方式进行编程,但是如何使用Java Streams以功能方式进行编程呢?我尝试过类似的东西,但这当然给了我一个.我能找到的所有例子,都用在基元或字符串上。people.stream.collect(groupingBy(Person::getLanguagesSpoken))Map<List<String>, List<Person>>groupingBy


答案 1

您可以将实例分解为语言对和(使用 ),然后根据需要对它们进行分组:PersonPersonflatMap

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())));

答案 2

这也可以在没有流的情况下完成,仍然使用java-8新功能。

people.forEach(x -> {
        x.getLanguagesSpoken().forEach(lang -> {
            langPersons.computeIfAbsent(lang, ignoreMe -> new ArrayList<>()).add(x);
        });
});

推荐