有没有一种优雅的方法可以将Map <p,optional <q =“” >>转换为稀疏Map <p,q =“”>?

时间:2019-02-18 12:47:06

标签: java java-8 functional-programming java-stream

是否有一种优雅的方法可以将payments转换为稀疏的Map<P, Optional<Q>>

这应该可以,但是有点麻烦:

Map<P, Q>

4 个答案:

答案 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.computeremappingFunction时删除/忽略键的映射。产生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.computenull获得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()));