有没有一种优雅的方法可以将Map<P,可选<Q>>转换为稀疏Map<P,Q>?

有没有一种优雅的方法将a转换为稀疏?Map<P, Optional<Q>>Map<P, Q>

这应该有效,但它有点meh:

Map<P,Optional<Q>> map = ...;
Map<P,Q> map2 = map.entrySet()
                  .stream().filter(e -> e.getValue().isPresent())
                  .collect(Collectors.toMap(e -> e.getKey(), e->e.getValue().get()));

答案 1

我会说你的方式几乎已经是最优雅的方式,我只会做一些轻微的外观改变,并用.这只是一个很小的变化,但比其他lambda更好地传达了你的意图e -> e.getKey()Entry::getKey

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()));

为什么其他解决方案不是更好/更优雅?

因为它们不是更简洁,而且它们再次陷入了不声明你想要做什么,而是如何做的陷阱,这在程序风格中很常见,但在功能风格中却不那么常见。

如果你看一下上面的代码,它几乎是不言自明的,并且有很好的流程。首先有一个带有 s 的非稀疏映射,然后声明不带 s 的稀疏映射,然后描述前者映射到后者的转换。这也没有副作用。仅当收集器实际完成时,才会分配稀疏映射。OptionalOptional

如果你看看其他解决方案,那些颠倒逻辑流并使用程序式思维方式:

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))

由于类型推断,您可以节省一些字符,但最终,如果您合理地格式化它们,则两种解决方案都需要4个LOC,因此它们不会比功能解决方案短。它们也不是更清楚。相反,它们依赖于在另一张地图中引起副作用。这意味着在计算过程中,您将获得分配给变量的部分构造的稀疏映射。使用功能解决方案,仅当正确构造映射时才会分配映射。这只是一个小的挑剔,在这种情况下可能不会引起问题,但对于其他可能变得相关的情况(例如,当涉及并发时),特别是当另一个映射不是局部变量而是字段时 - 或者更糟的是,从其他地方传入。foreach

此外,函数方法的扩展性更好 - 如果您有大量数据,切换到并行流是微不足道的,将-方法转换为并行无论如何都需要重写为功能/方法。这与这种轻量级操作无关(实际上,不要在这里这样做,它可能会更慢),但在其他情况下,这可能是一个理想的特征。foreachfiltercollect

在我看来,使用函数式/方法比使用程序式更好,因为您训练自己使用良好的习惯。但请记住,“优雅”往往在旁观者的眼中。对我来说,更“优雅”的方式是没有副作用的适当功能方式。新浪网.filtercollectforeach


答案 2

怎么样:

Map<P, Q> map2 = new HashMap<>();
map.forEach((key, opt) -> opt.ifPresent(value -> map2.put(key, value)));