收集以映射跳过空键/值

2022-09-03 14:02:39

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

stream.collect(Collectors.toMap(this::func1, this::func2));

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

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

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


答案 1

如果要避免计算函数和两次,则必须存储结果。例如: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));

这不会使代码更短,甚至效率也取决于具体情况。如果评估的成本足够高,可以补偿临时对象的创建,则此更改是值得的。原则上,临时对象可以得到优化,但这并不能保证。func1func2

从Java 9开始,你可以用Map.entry(...)替换。由于此条目类型从一开始就不允许,因此在构造条目之前需要对其进行筛选:new AbstractMap.SimpleImmutableEntry<>(…)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));

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


答案 2

避免两次计算函数的另一种方法。使用您选择的对类。不像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));
}

(选择适当的名称 - 我不知道你使用什么类型)


推荐