收集以跳过空键/值的映射

Joe*_*oel 5 java java-8 java-stream

假设我有一些流,想收集到这样的地图

stream.collect(Collectors.toMap(this::func1, this::func2));
Run Code Online (Sandbox Code Playgroud)

但我想跳过空键/值.当然,我可以这样做

stream.filter(t -> func1(t) != null)
    .filter(t -> func2(t) != null)
    .collect(Collectors.toMap(this::func1, this::func2));
Run Code Online (Sandbox Code Playgroud)

但是有更美观/有效的解决方案吗?

Hol*_*ger 6

如果要避免评估函数func1func2两次,则必须存储结果.例如

stream.map(t -> new AbstractMap.SimpleImmutableEntry<>(func1(t), func2(t))
      .filter(e -> e.getKey()!=null && e.getValue()!=null)
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Run Code Online (Sandbox Code Playgroud)

这不会使代码更短,甚至效率也取决于具体情况.这种变化不负有心人,如果评估的成本func1func2高到足以弥补临时对象的创建.原则上,临时对象可以被优化掉,但这不能保证.

从Java 9开始,您可以替换new AbstractMap.SimpleImmutableEntry<>(…)Map.entry(…).由于此条目类型null从一开始就不允许,因此在构造条目之前需要进行过滤:

stream.flatMap(t -> {
          Type1 value1 = func1(t);
          Type2 value2 = func2(t);
          return value1!=null && value2!=null? Stream.of(Map.entry(value1, value2)): null;
      })
      .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用您已经使用的库之一的对类型(Java API本身不提供这样的类型).

  • @Michael从Java 9开始,你可以使用更简单的`Map.entry(...)`.我在答案中添加了一个注释.它不仅源代码更短,结果是[基于价值](https://docs.oracle.com/javase/9​​/docs/api/java/lang/doc-files/ValueBased.html)使其均匀更适合临时物品. (2认同)
  • 在[`flatMap`]的情况下不需要@davidxxx(https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#flatMap-java.util.function.函数 - )映射到流,而不是元素:"*如果映射的流是'null`,则使用空流,而不是.*" (2认同)

Mic*_*ael 5

另一种避免对函数求值两次的方法。使用您选择的配对类。不像Holger那样简洁,但密度稍低一些,更容易阅读。

stream.map(A::doFuncs)
    .flatMap(Optional::stream)
    .collect(Collectors.toMap(Pair::getKey, Pair::getValue));

private static Optional<Pair<Bar, Baz>> doFuncs(Foo foo)
{
    final Bar bar = func1(foo);
    final Baz baz = func2(foo);
    if (bar == null || baz == null) return Optional.empty();
    return Optional.of(new Pair<>(bar, baz));
}
Run Code Online (Sandbox Code Playgroud)

(选择正确的名称 - 我不知道你使用的是什么类型)

  • 你也可以将 `doFuncs` 实现为 `returnOptional.ofNullable(func1(foo)) .flatMap(v1 -&gt;Optional.ofNullable(func2(foo)).map(v2 -&gt; new Pair&lt;&gt;(v1, v2)) );`; 这也将与“Map.entry”协调一致,因为在构造对/条目时,这两个值都已经保证为非“null”。 (2认同)