从 Java 8 中的对象流优雅地创建对象字段为 key/value 的映射
Elegantly create map with object fields as key/value from object stream in Java 8
我有以下class
class Person {
public String name;
public int age;
public List<String> hobbies;
Person(String name, int age, List<String> hobbies)
{this.name = name; this.age = age; this.hobbies = hobbies;}
}
如何创建 Map<Integer, Set<String>>
等爱好的年龄映射?
我想出的 Java 8 种方法是:
Map<Integer, Set<String>> collect8 = persons.stream()
.collect(
toMap(
p -> p.age,
p -> p.hobbies.stream().collect(toSet()),
(hobbies1, hobbies2) ->
Stream.concat(hobbies1.stream(), hobbies2.stream()).collect(toSet())
)
);
也许 Collectors.groupingBy()
有更惯用的方法吗?
作为一个相关问题,我发现没有 Java 流的版本更具可读性。
Map<Integer, Set<String>> collect7 = new HashMap<>();
for(Person p: persons) {
Set<String> hobbies = collect7.getOrDefault(p.age, new HashSet<>());
hobbies.addAll(p.hobbies);
collect7.put(p.age, hobbies);
}
如果更容易阅读,我们是否应该使用非流代码?特别是当流式版本(如此处所示)没有带有数据转换的中间流但在终端操作中很快结束时?
查看 Java 8 收集器文档中的类似示例:
Map<City, Set<String>> namesByCity
= people.stream().collect(groupingBy(Person::getCity, TreeMap::new,
mapping(Person::getLastName, toSet())));
您可以在此处使用相同的方法。
正如您自己所说:Stream
解决方案可能不如您当前的非 Stream
解决方案可读。使用 groupingBy
解决您的问题可能不像您预期的那样好,因为您希望将 List
转换为 Set
.
我用 groupingBy
、mapping
和 reducing
构建了一个解决方案,但该解决方案并不那么容易阅读,甚至包含错误。您可以在以下位置阅读更多相关信息: 我真的建议查看 Holger 给出的答案,因为它还包含一个更简单的解决方案,使用自定义 Collector
和 Java 9's flatMapping
,这对我来说接近你的非Stream
-解决方案。
但是我想出的另一个使用 groupingBy
的解决方案如下:
Map<Integer, Set<String>> yourmap;
yourmap = personList.stream()
.flatMap(p -> p.hobbies.stream()
.flatMap(hobby -> Stream.of(new SimpleEntry<>(p.age, hobby)))
)
.collect(Collectors.groupingBy(Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toSet())));
为此,您需要导入以下内容:
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
当然你也可以选择Tuple
或Pair
或者你最喜欢的。
但话又说回来,在任何方面都没有更好。
我会继续使用您当前的非 Stream
解决方案。它更具可读性并且做了它应该做的事情。
我有以下class
class Person {
public String name;
public int age;
public List<String> hobbies;
Person(String name, int age, List<String> hobbies)
{this.name = name; this.age = age; this.hobbies = hobbies;}
}
如何创建 Map<Integer, Set<String>>
等爱好的年龄映射?
我想出的 Java 8 种方法是:
Map<Integer, Set<String>> collect8 = persons.stream()
.collect(
toMap(
p -> p.age,
p -> p.hobbies.stream().collect(toSet()),
(hobbies1, hobbies2) ->
Stream.concat(hobbies1.stream(), hobbies2.stream()).collect(toSet())
)
);
也许 Collectors.groupingBy()
有更惯用的方法吗?
作为一个相关问题,我发现没有 Java 流的版本更具可读性。
Map<Integer, Set<String>> collect7 = new HashMap<>();
for(Person p: persons) {
Set<String> hobbies = collect7.getOrDefault(p.age, new HashSet<>());
hobbies.addAll(p.hobbies);
collect7.put(p.age, hobbies);
}
如果更容易阅读,我们是否应该使用非流代码?特别是当流式版本(如此处所示)没有带有数据转换的中间流但在终端操作中很快结束时?
查看 Java 8 收集器文档中的类似示例:
Map<City, Set<String>> namesByCity
= people.stream().collect(groupingBy(Person::getCity, TreeMap::new,
mapping(Person::getLastName, toSet())));
您可以在此处使用相同的方法。
正如您自己所说:Stream
解决方案可能不如您当前的非 Stream
解决方案可读。使用 groupingBy
解决您的问题可能不像您预期的那样好,因为您希望将 List
转换为 Set
.
我用 groupingBy
、mapping
和 reducing
构建了一个解决方案,但该解决方案并不那么容易阅读,甚至包含错误。您可以在以下位置阅读更多相关信息:Collector
和 Java 9's flatMapping
,这对我来说接近你的非Stream
-解决方案。
但是我想出的另一个使用 groupingBy
的解决方案如下:
Map<Integer, Set<String>> yourmap;
yourmap = personList.stream()
.flatMap(p -> p.hobbies.stream()
.flatMap(hobby -> Stream.of(new SimpleEntry<>(p.age, hobby)))
)
.collect(Collectors.groupingBy(Entry::getKey,
Collectors.mapping(Entry::getValue, Collectors.toSet())));
为此,您需要导入以下内容:
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
当然你也可以选择Tuple
或Pair
或者你最喜欢的。
但话又说回来,在任何方面都没有更好。
我会继续使用您当前的非 Stream
解决方案。它更具可读性并且做了它应该做的事情。