首页 > 解决方案 > 具有自定义逻辑的 Java 8 Stream groupingBy

问题描述

我有一个清单Records。其中有两个字段:LocalDateTime instant和一个Double data.

我想按小时对所有记录进行分组并创建一个Map<Integer, Double>. 其中键(整数)是小时,值(双精度)是该小时的最后一个数据 - 该小时的第一个数据。

到目前为止我所做的如下:

Function<Record, Integer> keyFunc = rec->rec.getInstant().getHour();
Map<Integer, List<Record>> valueMap = records.stream().collect(Collectors.groupingBy(keyFunc));

我希望值图保持不变Double,而不是List<Records>.

例如:列表记录可以如下:

Instant            Data
01:01:24           23.7
01:02:34           24.2
01:05:23           30.2
...
01:59:27           50.2
02:03:23           54.4
02:04:23           56.3
...
02:58:23           70.3
...

ETC

生成的地图应该是:

Key       Value
1          26.5 (50.2-23.7)
2          15.9 (70.3-54.4)
...

标签: javajava-8java-stream

解决方案


您主要是Collectors.mappinggroupingBy.

Map<Integer, List<Double>> valueMap = records.stream()
        .collect(Collectors.groupingBy(keyFunc, 
                Collectors.mapping(Record::getData, Collectors.toList())));

这会将Records 按他们instant的小时和这些记录的相应数据分组到List地图的值中。根据进一步的评论

我想从最后一个数据中减去第一个数据

是的,列表将根据即时排序列表

您可以使用分组映射来获得所需的输出:

Map<Integer, Double> output = new HashMap<>();
valueMap.forEach((k, v) -> output.put(k, v.get(v.size() - 1) - v.get(0)));

或者,您可以使用Collectors.mappingwithCollectors.collectingAndThen进一步作为:

Map<Integer, Double> valueMap = records.stream()
        .collect(Collectors.groupingBy(keyFunc,
                Collectors.mapping(Record::getData, 
                        Collectors.collectingAndThen(
                                Collectors.toList(), recs -> recs.get(recs.size() - 1) - recs.get(0)))));

推荐阅读