Сначала немного кода контекста:
import java.util.*;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.function.Function;
import java.util.stream.Collectors;
class Scratch {
static enum Id {A, B, C}
static class IdWrapper {
private final Id id;
public IdWrapper(Id id) {this.id = id;}
Id getId() { return id; }
}
public static void main(String[] args) {
Map<String, Object> v1 = new HashMap<>();
v1.put("parents", new HashSet<>(Arrays.asList(new IdWrapper(Id.A), new IdWrapper(Id.B))));
v1.put("size", 1d);
Map<String, Object> v2 = new HashMap<>();
v2.put("parents", new HashSet<>(Arrays.asList(new IdWrapper(Id.B), new IdWrapper(Id.C))));
v2.put("size", 2d);
Map<String, Map<String, Object>> allVs = new HashMap<>();
allVs.put("v1", v1);
allVs.put("v2", v2);
Выше представлена структура данных, с которой я имею дело.У меня есть внешняя карта (тип ключа не имеет значения), которая содержит внутренние «карты свойств» в качестве значений.Эти внутренние карты используют строки для поиска различного рода данных.
В случае, над которым я работаю, каждый v1, v2, ... представляет "диск".Каждый диск имеет определенный размер, но может иметь несколько родителей.
Теперь мне нужно суммировать размеры на родительский идентификатор как Map<Id, Double>
.Для приведенного выше примера эта карта будет {B=3.0, A=1.0, C=2.0}
.
Следующий код дает ожидаемый результат:
HashMap<Id, DoubleAdder> adders = new HashMap<>();
allVs.values().forEach(m -> {
double size = (Double) m.get("size");
Set<IdWrapper> wrappedIds = (Set<IdWrapper>) m.get("parents");
wrappedIds.forEach(w -> adders.computeIfAbsent(w.getId(), a -> new DoubleAdder()).add(size));
});
System.out.println(adders.keySet().stream()
.collect(Collectors.toMap(Function.identity(), key -> adders.get(key).doubleValue())));
Но код выглядит довольно неуклюжим (как тот факт, что мне нуженвторая карта для сложения размеров).
У меня есть похожий случай, когда всегда есть ровно один родитель, и это легко можно решить с помощью
collect(Collectors.groupingBy(...), Collectors.summingDouble(...);
Но я проиграл в случае с «множественными» родителями.
Итак, вопрос: можно ли переписать приведенное выше преобразование для вычисления требуемого Map<Id, Double>
, используя groupingBy()
?
И просто для записи: приведенное выше - просто mcve для задачи Iнужен ответ для.Я понимаю, что «макет данных» может выглядеть странно.На самом деле, у нас есть отдельные классы, представляющие, например, эти «диски».Но наша «структура» также позволяет получить доступ к свойствам любого объекта в базе данных, используя такие идентификаторы и имена свойств.И иногда, когда у вас возникают проблемы с производительностью, выборка данных таким способом «карты необработанных свойств» на несколько порядков быстрее по сравнению с доступом к самим настоящим «дисковым» объектам.Другими словами: я не могу ничего изменить в контексте.Мой вопрос касается только переписывания этих вычислений.
(я ограничен Java8 и «стандартными» библиотеками Java, но дополнительные ответы для более новых версий Java или хорошие нестандартные способы решения этой проблемыбудет оценен тоже)