私はこのようなクラスを持っています:
class MultiDataPoint {
private DateTime timestamp;
private Map<String, Number> keyToData;
}
そして、各MultiDataPointについて、を生成したい
class DataSet {
public String key;
List<DataPoint> dataPoints;
}
class DataPoint{
DateTime timeStamp;
Number data;
}
もちろん、「キー」は複数のMultiDataPointsで同じにすることができます。
したがって、List<MultiDataPoint>
、どうすればJava 8ストリームを使用してList<DataSet>
?
これは私が現在ストリームなしで変換を行っている方法です:
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();
}
同じ結果を達成するための多くの異なるアプローチがあることを示しているため、これは興味深い質問です。以下に3つの異なる実装を示します。
コレクションフレームワークのデフォルトメソッド: Java 8は、コレクションクラスにいくつかのメソッドを追加しましたが、これらはStream APIに直接関連していません。これらのメソッドを使用すると、非ストリーム実装の実装を大幅に簡素化できます。
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が提供するソリューションとほぼ同じです。彼のソリューションとは対照的に、次の実装では、中間データ構造として匿名内部クラスを使用しています。
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:元のデータ構造をフラット化する代わりに、それぞれにMapを作成することもできますMultiDataPointしてから、すべてのマップをマージしますリデュース操作で単一のマップに。コードは上記のソリューションよりも少し単純です:
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());
}
map mergerの実装はCollectorsクラス内にあります。残念ながら、外部からアクセスするには少し注意が必要です。以下は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;
};
}
これを行うには、中間データ構造を考え出す必要がありました。
class KeyDataPoint {
String key;
DateTime timestamp;
Number data;
// obvious constructor and getters
}
これを導入すると、各MultiDataPointを(タイムスタンプ、キー、データ)トリプルのリストに「フラット化」し、MultiDataPointのリストからそのようなトリプルをすべて一緒にストリーミングします。
次に、各キーのデータを収集するために、文字列キーにgroupingBy
操作を適用します。単純なgroupingBy
は、各文字列キーから対応するKeyDataPointトリプルのリストへのマップになることに注意してください。トリプルは必要ありません。 (タイムスタンプ、データ)のペアであるDataPointインスタンスが必要です。これを行うには、KeyDataPointトリプルから適切な値を取得して新しいDataPointを構築するgroupingBy
操作であるmapping
の「ダウンストリーム」コレクターを適用します。 mapping
操作のダウンストリームコレクターは、単にtoList
であり、同じグループのDataPointオブジェクトをリストに収集します。
これで、Map<String, List<DataPoint>>
そして、それをDataSetオブジェクトのコレクションに変換します。マップエントリをストリーム出力し、DataSetオブジェクトを構築し、それらをリストに収集して返します。
コードは最終的に次のようになります。
Collection<DataSet> convertMultiDataPointToDataSet(List<MultiDataPoint> multiDataPoints) {
return multiDataPoints.stream()
.flatMap(mdp -> mdp.getData().entrySet().stream()
.map(e -> new KeyDataPoint(e.getKey(), mdp.getTimestamp(), e.getValue())))
.collect(groupingBy(KeyDataPoint::getKey,
mapping(kdp -> new DataPoint(kdp.getTimestamp(), kdp.getData()), toList())))
.entrySet().stream()
.map(e -> new DataSet(e.getKey(), e.getValue()))
.collect(toList());
}
私はコンストラクターとゲッターといくつかの自由を取りましたが、それらは明白であるべきだと思います。