使用 java 8 流将键值对转换为按键对象映射分组的最快方法



Model:

public class AgencyMapping {
private Integer agencyId;
private String scoreKey;
}
public class AgencyInfo {
private Integer agencyId;
private Set<String> scoreKeys;
}

我的代码:

List<AgencyMapping> agencyMappings;
Map<Integer, AgencyInfo> agencyInfoByAgencyId = agencyMappings.stream()
.collect(groupingBy(AgencyMapping::getAgencyId,
collectingAndThen(toSet(), e -> e.stream().map(AgencyMapping::getScoreKey).collect(toSet()))))
.entrySet().stream().map(e -> new AgencyInfo(e.getKey(), e.getValue()))
.collect(Collectors.toMap(AgencyInfo::getAgencyId, identity()));

有没有办法获得相同的结果并使用更简单、更快的代码?

您可以通过调用mapping(AgencyMapping::getScoreKey, toSet())来简化对collectingAndThen(toSet(), e -> e.stream().map(AgencyMapping::getScoreKey).collect(toSet()))))的调用。

Map<Integer, AgencyInfo> resultSet = agencyMappings.stream()
.collect(groupingBy(AgencyMapping::getAgencyId,
mapping(AgencyMapping::getScoreKey, toSet())))
.entrySet()
.stream()
.map(e -> new AgencyInfo(e.getKey(), e.getValue()))
.collect(toMap(AgencyInfo::getAgencyId, identity()));

使用toMap收集器查看它的另一种方式:

Map<Integer, AgencyInfo> resultSet = agencyMappings.stream()
.collect(toMap(AgencyMapping::getAgencyId, // key extractor
e -> new HashSet<>(singleton(e.getScoreKey())), // value extractor
(left, right) -> { // a merge function, used to resolve collisions between values associated with the same key
left.addAll(right);
return left;
}))
.entrySet()
.stream()
.map(e -> new AgencyInfo(e.getKey(), e.getValue()))
.collect(toMap(AgencyInfo::getAgencyId, identity()));

后一个例子可以说比前一个例子更复杂。尽管如此,您的方法几乎是除了使用mapping而不是如上所述的collectingAndThen

之外的方法。除此之外,我没有看到您可以使用所示代码简化的任何其他内容。

至于更快的代码,如果你建议你当前的方法性能很慢,那么你可能需要阅读这里的答案,这些答案谈到了什么时候你应该考虑并行。

您正在收集到中间映射,然后将此映射的条目流式传输到AgencyInfo实例,这些实例最终收集到另一个映射。

除了所有这些之外,您还可以使用Collectors.toMap直接收集到地图,将每个AgencyMapping对象映射到所需的AgencyInfo并根据需要合并scoreKeys

Map<Integer, AgencyInfo> agencyInfoByAgencyId = agencyMappings.stream()
.collect(Collectors.toMap(
AgencyMapping::getAgencyId,
mapping -> new AgencyInfo(
mapping.getAgencyId(), 
new HashSet<>(Set.of(mapping.getScoreKey()))),
(left, right) -> {
left.getScoreKeys().addAll(right.getScoreKeys());
return left;
}));

这的工作原理是按AgencyMapping::getAgencyId对流的AgencyMapping元素进行分组,但将AgencyInfo对象存储在映射中。我们通过手动映射每个原始AgencyMapping对象来获取这些AgencyInfo实例。最后,我们通过合并函数合并地图中已有AgencyInfo实例,该函数将左scoreKeys从一个AgencyInfo折叠到另一个。

我正在使用Java 9的Set.of创建一个单例集。如果您没有 Java 9,则可以将其替换为Collections.singleton

最新更新