Gho*_*ica 6 java grouping java-8 java-stream
首先,一些上下文代码:
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);
Run Code Online (Sandbox Code Playgroud)
以上代表我正在处理的数据结构.我有一个外部地图(键类型无关紧要),它包含内部"属性映射"作为值.这些内部映射使用字符串来查找不同类型的数据.
在我正在处理的情况下,每个v1,v2,...代表一个"磁盘".每个磁盘都有特定的大小,但可以有多个父级.
现在我需要将每个父ID 的大小总结为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())));
Run Code Online (Sandbox Code Playgroud)
但代码感觉非常笨重(就像我需要第二张地图来添加大小).
我有一个类似的情况,总是只有一个父,并且可以很容易地使用
collect(Collectors.groupingBy(...), Collectors.summingDouble(...);
Run Code Online (Sandbox Code Playgroud)
但我因"多重"父母案而迷失.
那么,问题:可以使用上面的转换计算所需的Map<Id, Double>重写groupingBy()吗?
只是为了记录:上面只是我需要答案的问题的一个mcve.据我所知,"数据布局"可能看起来很奇怪.实际上,我们实际上有不同的类代表这些"磁盘".但是我们的"框架"还允许使用这些ID和属性名访问数据库中任何对象的属性.有时,当您遇到性能问题时,与访问真正的"磁盘"对象本身相比,以这种"原始属性映射"方式获取数据的速度要快几个数量级.换句话说:我无法改变任何有关上下文的内容.我的问题只是重写计算.
(我受限于Java8和"标准"Java库,但是对于新版本Java或者解决此问题的非标准方法的其他答案也将受到赞赏)
这是一个单流管道解决方案:
Map<Id,Double> sums = allVs.values ()
.stream ()
.flatMap (m -> ((Set<IdWrapper>)m.get ("parents")).stream ()
.map (i -> new SimpleEntry<Id,Double>(i.getId(),(Double)m.get ("size"))))
.collect (Collectors.groupingBy (Map.Entry::getKey,
Collectors.summingDouble (Map.Entry::getValue)));
Run Code Online (Sandbox Code Playgroud)
输出:
{B=3.0, A=1.0, C=2.0}
Run Code Online (Sandbox Code Playgroud)
我们的想法是将每个内部转换Map为一个Stream条目,其中键是Id("父" Set),并且值是相应的"大小".
然后很容易将其分组Stream到所需的输出中.
| 归档时间: |
|
| 查看次数: |
100 次 |
| 最近记录: |