How to map to multiple elements with Java 8 streams?(如何使用 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;
}
当然,一个键"在多个 MultiDataPoints 中可以是相同的.
of course a 'key' can be the same across multiple MultiDataPoints.
那么给定一个 List<MultiDataPoint>
,我如何使用 Java 8 流转换为 List<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.
集合框架中的默认方法: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())));
}
<小时>
Stream API with map merging:您也可以为每个MultiDataPoint创建一个Map,而不是扁平化原始数据结构,然后使用 reduce 操作将所有映射合并为一个映射.代码比上面的方案简单一点:
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());
}
您可以在 Collectors 类中找到 map 合并 的实现.不幸的是,从外部访问它有点棘手.以下是地图合并的另一种实现:
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 流映射到多个元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何使用 Java 8 流映射到多个元素?
基础教程推荐
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01
- 如何对 HashSet 进行排序? 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01