【问题标题】:Fastest way to convert key value pairs to grouped by key objects map using java 8 stream使用 java 8 流将键值对转换为按键对象映射分组的最快方法
【发布时间】:2018-03-25 14:11:35
【问题描述】:

型号:

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

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

【问题讨论】:

    标签: performance collections java-8 java-stream


    【解决方案1】:

    您可以通过调用mapping(AgencyMapping::getScoreKey, toSet()) 来简化对collectingAndThen(toSet(), e -&gt; 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 相比,几乎是一种不同的方法。

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

    至于 faster 代码,如果您建议您当前的方法在性能上很慢,那么您可能需要阅读here 的答案,这些答案谈到了您应该考虑何时考虑并行化。

    【讨论】:

    • 感谢您的建议:mapping(AgencyMapping::getScoreKey, toSet())))
    【解决方案2】:

    您正在收集到一个中间映射,然后流式传输此映射的条目以创建 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 元素按AgencyMapping::getAgencyId 分组来实现的,但将AgencyInfo 对象存储在映射中。我们通过手动映射每个原始AgencyMapping 对象来获得这些AgencyInfo 实例。最后,我们通过 merge 函数 合并地图中已经存在的AgencyInfo 实例,该函数向左折叠 scoreKeys 从一个AgencyInfo 到另一个。

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

    【讨论】:

    • 欢迎朋友!尽管考虑到还有“singletonList”和“singletonMap”,但您对“singletonSet”的说法是可以理解的。他们没有将其称为“singletonSet”,这感觉不一致。
    • @Aominè 实际上,我记得有一个问题确切地询问了这种不一致。答案类似于根据定义,单例是仅包含一个元素的集合(包括指向维基百科的链接)。提出问题的人回答如下:是的,我知道,但是为什么名称不一致? 有人以一种不太好的方式回答说你没有说单例集,因为 set 隐含在单例等中。这很有趣(大约 20 秒)...
    猜你喜欢
    • 2023-03-15
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多