Java 是否有一种优雅的方式来转换地图<;P、 可选<;Q>&燃气轮机;到稀疏贴图<;P、 Q>;?

Java 是否有一种优雅的方式来转换地图<;P、 可选<;Q>&燃气轮机;到稀疏贴图<;P、 Q>;?,java,java-8,functional-programming,java-stream,Java,Java 8,Functional Programming,Java Stream,有没有一种优雅的方法可以将贴图转换为稀疏的贴图 这应该行得通,但有点麻烦: Map<P,Optional<Q>> map = ...; Map<P,Q> map2 = map.entrySet() .stream().filter(e -> e.getValue().isPresent()) .collect(Collectors.toMap(e -> e.getKey(),

有没有一种优雅的方法可以将
贴图
转换为稀疏的
贴图

这应该行得通,但有点麻烦:

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()));
Map=。。。;
Map map2=Map.entrySet()
.stream().filter(e->e.getValue().isPresent())
.collect(Collectors.toMap(e->e.getKey(),e->e.getValue().get());

创建新映射并通过迭代原始映射添加值时,会更简单:

Map<P, Q> map2 = new HashMap<>();
map.forEach((p, q) -> map2.compute(p, (k, v) -> q.orElse(null)));
输出是
{four=4,one=1,two=2}
(当
map2.compute
q.orElse
获取
null
,则
p
未添加到
map2

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

我会坚持你所拥有的。它直接表达了意图,并利用了流媒体,这是其他答案所缺乏的质量。有时,
是冗长的,您无能为力

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

我想说你的方式几乎已经是最优雅的方式了,我只会做一些细微的修饰,用
Entry::getKey
替换你的收集器中的
e->e.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()));
这仅略短于:

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))
Map=[…];
Map sparseMap=newhashmap();
对于(条目e:map.entrySet())e.getValue().ifPresent(value->sparseMap.put(key,value))
由于类型推断,您可以节省一些字符,但最终,如果您合理地设置它们的格式,两个
foreach
解决方案都需要4个LOC,因此它们不会比函数解决方案短。他们也不清楚。相反,它们依赖于在另一张地图上引起副作用。这意味着在计算过程中,您将得到一个部分构造的稀疏映射,并将其分配给变量。对于函数式解决方案,只有在正确构造贴图时才会指定贴图。这只是一个小小的吹毛求疵,在本例中可能不会引起问题,但对于其他可能与之相关的情况(例如,当涉及并发性时),尤其是当另一个映射不是局部变量而是字段时,或者更糟糕的是,从其他地方传入时,应该记住这一点

此外,功能性方法可以更好地扩展-如果您有大量数据,则切换到并行流是很简单的,将
foreach
-方法转换为并行需要重写到功能性
过滤器
/
收集
方法。这与这种轻量级操作无关(事实上,不要在这里执行,它可能会更慢),但在其他情况下,这可能是一个理想的特性


在我看来,使用功能性的
过滤器
/
收集
方法比使用过程性的
foreach
方法更可取,因为你会训练自己使用好习惯。但请记住,“优雅”往往在旁观者的眼中。对我来说,更“优雅”的方式是正确的功能方式,没有副作用。YMMV.

可能:
map.forEach((键,可选)->optional.ifPresent(value->map2.put(键,值))但是必须首先初始化
map2
map2=newhashmap()@nullpointer我看到那个特别的方面吸引了注意。请阅读我在这篇文章中的最后一句话:-)@ernest_k仍然
null
看起来有点恶心。如果可能的话,我会避免的。@ZhekaKozlov我可能解释得不对。它不是插入空值。我将添加一个示例。它很短,但我觉得不太可读。如果我在一个更大的代码库中看到这一点,它的作用将不会立即变得明显。@JohnKugelman感谢您的意见。可读性是相对的,有时是次要的。虽然并行化流的能力在理论上很好,但我非常希望看到并行流比顺序流(或直接在映射上迭代)更有效地执行此任务的映射。一般来说,当条目必须经过一些可以在多个线程之间有效共享的缓慢的非平凡处理时,并行流非常有用。然而,仅仅从可选项中提取一个值是一个轻量级的操作,并行化的开销可能会大大超过操作本身的成本。为什么不在收集到映射之前用映射将条目转换为条目?
map.forEach
“只稍微短一点”而不是
for
循环,因为您使用的是原始类型
条目
。正确的解决方案必须使用
条目
,因此代码大小的差异取决于实际的
P
Q
@IlmariKaronen,这在本例中是不相关的。这就是我指出养成良好习惯的原因。因为当你遇到需要它的情况时,你不需要与你正在编写的代码不同的代码。@Holger true会稍微改变这一点
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))