繁体   English   中英

从地图中删除值为空的所有条目

[英]Remove all entries where value is an empty Optional from map

我想从地图中删除value为空的所有条目。 看起来似乎并不复杂,但我试图找到一个更好的解决方案。


输入:

我有以下地图:

Map<String, Function<String, Optional<String>>> attributesToCalculate = new HashMap<>();

其中key - 只是一个String和value - 对返回Optional <String>的方法的引用


输出:

结果,我想得到

Map<String, String> calculatedAttributes

(不包括值为空的条目可选)


这是我的解决方案

      return attributesToCalculate.entrySet()
        .stream()
        .map(entry -> Pair.of(entry.getKey(), entry.getValue().apply(someString)))
        .filter(entry -> entry.getValue().isPresent())
        .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().get()));

但是我不喜欢.filter部分因为那时我必须在collect部分中的Optional上调用.get()

是否有更好的方法(可能没有.get调用)来解决这个问题? 谢谢。

如上所述,如果您已经检查过Optional不为空,则使用get没有任何问题。

但是,我认为这段代码最好不使用流表达。

Map<String, String> result = new HashMap<>();
attributesToCalculate.forEach((k, v) ->
    v.apply(someString).ifPresent(str -> result.put(k, str))
);

如果您不喜欢使用forEach以这种方式填充地图,则可以使用简单的for循环。

使用Guava,您可以这样做:

Maps.filterValues(
        Maps.transformValues(
                attributesToCalculate,
                f -> f.apply("someString").orElse(null)),
        Predicates.notNull())

请注意,这将返回基础映射的视图,这意味着任何查找或迭代都将委托给该函数。 如果这是一个问题,只需将结果复制到新地图。

或者您可以考虑StreamExEntryStream

EntryStream.of(attributesToCalculate)
        .mapValues(f -> f.apply("someString"))
        .flatMapValues(StreamEx::of)
        .toMap();

不是一个非常漂亮的,类似于for循环:

 return attributesToCalculate.entrySet().stream().collect(HashMap::new, (sink, entry) -> {

    entry.getValue().apply(someString).ifPresent(v -> sink.put(entry.getKey(), v));

}, Map::putAll);

您可以定义一个辅助方法来创建您的Pair:

public static <L, R> Optional<Pair<L, R>> of2(L left, Optional<R> right) {
    return right.map(r -> Pair.of(left, r));
}

虽然这意味着使用Optional作为参数,但在这种情况下它不是那么糟糕的imo(如果你内联方法,你不会把它作为参数...)。

然后你可以这样做:

attributesToCalculate.entrySet()
     .stream()
     .map(entry -> of2(entry.getKey(), entry.getValue().apply(someString)))
     .flatMap(Optional::stream) // Java 9
     .collect(Collectors.toMap(Map.Entry::getKey, Pair::getValue));

使用普通的旧Iterator什么问题? 这看起来并不像其他一些流解决方案那么冗长。

final Map<String, Optional<Object>> attributesToCalculate = new HashMap<>();
final Map<String, Object> calculatedAttributes = new HashMap<>();

final Iterator<Entry<String, Optional<Object>>> iter = attributesToCalculate.entrySet().iterator();
while (iter.hasNext()) {
    final Entry<String, Optional<Object>> current = iter.next();
    if (current.getValue().isPresent()) {
        calculatedAttributes.put(current.getKey(), current.getValue().get());
    }
}

或者使用plain for循环更短:

for(Entry<String, Optional<Object>> current : attributesToCalculate.entrySet()) {
    if (current.getValue().isPresent()) {
        calculatedAttributes.put(current.getKey(), current.getValue().get());
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM