是否有一种优雅的方法可以将payments
转换为稀疏的Map<P, Optional<Q>>
?
这应该可以,但是有点麻烦:
Map<P, Q>
答案 0 :(得分:10)
我要说的是,您的方式几乎已经是最优雅的方式,我只需要进行一些细微的修饰,然后用e -> e.getKey()
替换收藏家中的Entry::getKey
。这只是一个很小的变化,但传达的意图比其他lambda更好。
Map<P, Optional<Q>> map = new HashMap<>();
Map<P, Q> sparseMap = map.entrySet().stream()
.filter(e -> e.getValue().isPresent())
.collect(Collectors.toMap(Entry::getKey, e -> e.getValue().get()));
为什么其他解决方案没有更好/更优雅?
由于它们不够简洁,因此又陷入了陷阱,没有声明您要做什么,而是声明了方法,这在程序样式中很常见,但在功能性方面却没有那么多。
如果您看上面的代码,它几乎是不言自明的,并且流程流畅。首先,您有一个带有Optional
的非稀疏映射,然后声明一个没有Optional
的稀疏映射,然后描述从前一个映射到后者的转换。这也没有副作用。稀疏映射仅在收集器实际完成时分配。
如果您查看其他解决方案,则这些 invert 逻辑流程并使用过程性思维方式:
Map<P, Optional<Q>> map = [....];
Map<P, Q> sparseMap = new HashMap<>();
map.forEach((key, opt) -> opt.ifPresent(value -> sparseMap.put(key, value)));
这只是短于:
Map<P, Optional<Q>> map = [....];
Map<P, Q> sparseMap = new HashMap<>();
for (Entry<P, Optional<Q>> e : map.entrySet()) e.getValue().ifPresent(value -> sparseMap.put(key, value))
由于类型推断,您节省了一些字符,但最后,如果合理地格式化它们,则两个foreach
解决方案都需要4个LOC,因此它们的长度不比功能上的矮。他们也不清楚。在相反上,他们依靠在另一幅图中引起副作用。这意味着在计算期间,您会获得分配给变量的部分构造的稀疏映射。使用功能解决方案时,仅在正确构建地图后才分配地图。这只是一个小巧的选择,在这种情况下可能不会引起问题,但是在其他情况下(例如,涉及并发的情况)可能会引起注意,特别是在其他地图不是局部变量,但有一个字段-或更糟的是从其他地方传入的。
此外,功能性方法的伸缩性更好-如果您有大量数据,切换到并行流是微不足道的,将foreach
方法转换为并行方法需要重写为功能性filter
/ {{1 }}无论如何。这与此类轻量级操作无关(实际上,此处不这样做,它可能会更慢),但在其他情况下可能是理想的特性。
我认为,使用功能性collect
/ filter
方法比使用过程性collect
更好,因为您可以训练自己养成良好的习惯。但是请记住,“优雅”通常在情人眼中。对我来说,更“优雅”的方式是正确的功能方式,没有副作用。 YMMV。
答案 1 :(得分:9)
如何?
Map<P, Q> map2 = new HashMap<>();
map.forEach((key, opt) -> opt.ifPresent(value -> map2.put(key, value)));
答案 2 :(得分:5)
当您创建新地图并通过迭代原始地图来添加值时,它更简单:
Map<P, Q> map2 = new HashMap<>();
map.forEach((p, q) -> map2.compute(p, (k, v) -> q.orElse(null)));
将跳过value.isPresent()
将为其返回false
的所有原始条目(不包括在result
中):Map.compute
在remappingFunction
时删除/忽略键的映射。产生null
。
以下是一个说明如何处理null的示例:
Map<String, Optional<Integer>> map = new HashMap<>();
map.put("one", Optional.of(1));
map.put("two", Optional.of(2));
map.put("three", Optional.empty());
map.put("four", Optional.of(4));
map.put("five", Optional.empty());
Map<String, Integer> map2 = new HashMap<>();
map.forEach((p, q) -> map2.compute(p, (k, v) -> q.orElse(null)));
System.out.println(map2);
输出为{four=4, one=1, two=2}
(当map2.compute
从null
获得q.orElse
时,p
未添加到map2
中)
答案 3 :(得分:3)
我会坚持你所拥有的。它直接表达了意图,并利用了流式传输,这是其他答案所缺乏的质量。有时Stream
太冗长,您无能为力。
Map<P,Q> map2 = map.entrySet()
.stream().filter(e -> e.getValue().isPresent())
.collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().get()));