java-8 - 在深层属性上收集 groupBy
问题描述
private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
if (squares == null || squares.isEmpty()) {
return emptyMap();
}
Map<String, Set<Square>> res = new HashMap<>();
squares.stream()
.filter(square -> {
if (square.getZuloCodes().isEmpty()) {
LOG("Ignored {}", square.id);
return false;
}
return true;
})
.forEach(square -> {
square.getZuloCodes()
.forEach(code -> {
res.putIfAbsent(code, new HashSet<>());
res.get(code).add(square);
}));
});
return Collections.unmodifiableMap(res);
}
上面的代码接收一个 Squares 列表,这些 Squares 里面可能包含 ZuloCodes。输出应该是一个不可变的 Map zuloCode,并使用该 UniquePrefix 对所有正方形进行赋值。如您所见,我无法找到删除辅助集合 res 并使代码易于阅读的方法,有没有办法将该集合分解为 [zuloCode, square] 然后 collect.groupBy ?此外,如果过滤器内部如此难以阅读,您将如何解决它?
解决方案
标准方法是flatMap
在收集 using 之前使用 using groupingBy
,但由于您需要每个元素的原始Square
内容,因此您需要映射到同时包含Square
实例和 zulo 代码的对象String
。
由于 Java 中还没有标准对或元组类型,因此解决方法是使用实Map.Entry
例,如下所示
private Map<String, Set<Square>> populateZuloSquare0(List<Square> squares) {
if (squares == null || squares.isEmpty()) {
return emptyMap();
}
return squares.stream()
.filter(square -> logMismatch(square, !square.getZuloCodes().isEmpty()))
.flatMap(square -> square.getZuloCodes().stream()
.map(code -> new AbstractMap.SimpleEntry<>(code, square)))
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toSet())),
Collections::unmodifiableMap));
}
private static boolean logMismatch(Square square, boolean match) {
if(!match) LOG("Ignored {}", square.id);
return match;
}
另一种方法是使用自定义收集器,它将遍历键:
private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
if (squares == null || squares.isEmpty()) {
return emptyMap();
}
return squares.stream()
.filter(square -> logMismatch(square, !square.getZuloCodes().isEmpty()))
.collect(Collector.of(
HashMap<String, Set<Square>>::new,
(m,square) -> square.getZuloCodes()
.forEach(code -> m.computeIfAbsent(code, x -> new HashSet<>()).add(square)),
(m1,m2) -> {
if(m1.isEmpty()) return m2;
m2.forEach((key,set) ->
m1.merge(key, set, (s1,s2) -> { s1.addAll(s2); return s1; }));
return m1;
},
Collections::unmodifiableMap)
);
}
请注意,此自定义收集器可以看作是以下循环代码的并行变体:
private Map<String, Set<Square>> populateZuloSquare(List<Square> squares) {
if (squares == null || squares.isEmpty()) {
return emptyMap();
}
Map<String, Set<Square>> res = new HashMap<>();
squares.forEach(square -> {
if(square.getZuloCodes().isEmpty()) LOG("Ignored {}", square.id);
else square.getZuloCodes().forEach(
code -> res.computeIfAbsent(code, x -> new HashSet<>()).add(square));
});
return Collections.unmodifiableMap(res);
}
当您不需要代码具有并行能力时,现在看起来可能还不错……</p>
推荐阅读
- r - 计算每月的权重差异
- python - 在散点图上绘制高维数据 - 这可行吗?
- c++ - C++ 完美转发与 const 引用
- wpf - 如何绑定到 ListView 中的模板生成元素
- bash - 詹金斯错误或预期行为?管道上可能存在不同版本的 bash
- javascript - 带有 openweathermap API 的 Vue.js 应用程序中未捕获的语法错误
- java - 方法不可用,需要“不必要”的重写?
- c++ - 代码块 C 'Hello World':不运行,没有这样的文件或目录
- python - 在 django 面板中删除实例时出错
- angular - 如何在 Embed 印迹扩展中获取 quill 实例?