Ответ 1
Это интересный вопрос, потому что он показывает, что существует множество разных подходов к достижению одного и того же результата. Ниже я показываю три различные реализации.
Методы по умолчанию в Collection Framework:. 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 потока с объединением карт: Вместо того, чтобы сгладить исходные структуры данных, вы также можете создать карту для каждого 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());
}
Вы можете найти реализацию слияния карт в классе Collectors. К сожалению, немного сложно получить доступ к нему со стороны. Ниже приведена альтернативная реализация слияния карт:
<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;
};
}