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

时间:2018-03-25 14:11:35

标签: performance collections java-8 java-stream

型号:

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

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

2 个答案:

答案 0 :(得分:4)

您可以通过调用collectingAndThen(toSet(), e -> e.stream().map(AgencyMapping::getScoreKey).collect(toSet()))))来简化对mapping(AgencyMapping::getScoreKey, 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的方法。

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

对于更快的代码,如果您建议当前的方法性能较慢,那么您可能需要阅读答案here,该答案会在您考虑时考虑并行。

答案 1 :(得分:1)

您正在收集到中间地图,然后将此地图的条目流式传输以创建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个对象。我们通过手动映射每个原始AgencyInfo对象来获取这些AgencyMapping个实例。最后,我们通过合并函数合并AgencyInfo已经在地图中的scoreKeys个实例,向左折叠 AgencyInfo Set.of Collections.singleton 1}}到另一个。

我正在使用Java 9的subject = await Subject.find({ name: 'Math' }); 来创建单例集。如果您没有Java 9,则可以将其替换为Subject.find({name: 'Math'})