如何使用Java 8流映射到多个元素? [英] How to map to multiple elements with Java 8 streams?

查看:126
本文介绍了如何使用Java 8流映射到多个元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这样的课程:

class MultiDataPoint {
  private DateTime timestamp;
  private Map<String, Number> keyToData;
}

我希望为每个MultiDataPoint生成

and i want to produce , for each MultiDataPoint

class DataSet {
        public String key;    
        List<DataPoint> dataPoints;
}

class DataPoint{
  DateTime timeStamp;
  Number data;
}

当然,key在多个MultiDataPoints中可以相同。

of course a 'key' can be the same across multiple MultiDataPoints.

所以给定 List< MultiDataPoint> ,如何使用Java 8流转换为列表< DataSet>

So given a List<MultiDataPoint>, how do I use Java 8 streams to convert to List<DataSet>?

这就是我目前正在进行无流转换的方式:

This is how I am currently doing the conversion without streams:

Collection<DataSet> convertMultiDataPointToDataSet(List<MultiDataPoint> multiDataPoints)
{

    Map<String, DataSet> setMap = new HashMap<>();

    multiDataPoints.forEach(pt -> {
        Map<String, Number> data = pt.getData();
        data.entrySet().forEach(e -> {
            String seriesKey = e.getKey();
            DataSet dataSet = setMap.get(seriesKey);
            if (dataSet == null)
            {
                dataSet = new DataSet(seriesKey);
                setMap.put(seriesKey, dataSet);
            }
            dataSet.dataPoints.add(new DataPoint(pt.getTimestamp(), e.getValue()));
        });
    });

    return setMap.values();
}


推荐答案

这是一个有趣的问题,因为它表明有很多不同的方法可以实现相同的结果。下面我展示了三种不同的实现方式。

It's an interesting question, because it shows that there are a lot of different approaches to achieve the same result. Below I show three different implementations.

Collection Framework中的默认方法: Java 8添加了一些集合类的方法,与 Stream API 没有直接关系。使用这些方法,您可以显着简化非流实现的实现:

Default methods in Collection Framework: Java 8 added some methods to the collections classes, that are not directly related to the Stream API. Using these methods, you can significantly simplify the implementation of the non-stream implementation:

Collection<DataSet> convert(List<MultiDataPoint> multiDataPoints) {
    Map<String, DataSet> result = new HashMap<>();
    multiDataPoints.forEach(pt ->
        pt.keyToData.forEach((key, value) ->
            result.computeIfAbsent(
                key, k -> new DataSet(k, new ArrayList<>()))
            .dataPoints.add(new DataPoint(pt.timestamp, value))));
    return result.values();
}






流API扁平化和中间数据结构:以下实现几乎与Stuart Marks提供的解决方案相同。与他的解决方案相反,以下实现使用匿名内部类作为中间数据结构。


Stream API with flatten and intermediate data structure: The following implementation is almost identical to the solution provided by Stuart Marks. In contrast to his solution, the following implementation uses an anonymous inner class as intermediate data structure.

Collection<DataSet> convert(List<MultiDataPoint> multiDataPoints) {
    return multiDataPoints.stream()
        .flatMap(mdp -> mdp.keyToData.entrySet().stream().map(e ->
            new Object() {
                String key = e.getKey();
                DataPoint dataPoint = new DataPoint(mdp.timestamp, e.getValue());
            }))
        .collect(
            collectingAndThen(
                groupingBy(t -> t.key, mapping(t -> t.dataPoint, toList())),
                m -> m.entrySet().stream().map(e -> new DataSet(e.getKey(), e.getValue())).collect(toList())));
}






流API使用地图合并:您可以为每个 MultiDataPoint 创建地图,而不是展平原始数据结构,然后将所有地图合并到一个地图中减少操作。代码比上述解决方案简单一点:


Stream API with map merging: Instead of flattening the original data structures, you can also create a Map for each MultiDataPoint, and then merge all maps into a single map with a reduce operation. The code is a bit simpler than the above solution:

Collection<DataSet> convert(List<MultiDataPoint> multiDataPoints) {
    return multiDataPoints.stream()
        .map(mdp -> mdp.keyToData.entrySet().stream()
            .collect(toMap(e -> e.getKey(), e -> asList(new DataPoint(mdp.timestamp, e.getValue())))))
        .reduce(new HashMap<>(), mapMerger())
        .entrySet().stream()
        .map(e -> new DataSet(e.getKey(), e.getValue()))
        .collect(toList());
}

您可以找到地图合并的实现收藏家类中。不幸的是,从外部访问它有点棘手。以下是地图合并的替代实现:

You can find an implementation of the map merger within the Collectors class. Unfortunately, it is a bit tricky to access it from the outside. Following is an alternative implementation of the map merger:

<K, V> BinaryOperator<Map<K, List<V>>> mapMerger() {
    return (lhs, rhs) -> {
        Map<K, List<V>> result = new HashMap<>();
        lhs.forEach((key, value) -> result.computeIfAbsent(key, k -> new ArrayList<>()).addAll(value));
        rhs.forEach((key, value) -> result.computeIfAbsent(key, k -> new ArrayList<>()).addAll(value));
        return result;
    };
}

这篇关于如何使用Java 8流映射到多个元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆