如何在应用收集之前从流中删除 Null 元素?

How to delete Null-elements from the Stream before applying collect?

提问人:user504909 提问时间:7/1/2022 最后编辑:Alexander Ivanchenkouser504909 更新时间:7/1/2022 访问量:611

问:

我的流函数有时会返回,当我收集它们时如何删除该返回?nullnull

versions.stream().map(vs->{
        if(vs.matches("^matched string$")) {
                ...
                return new VersionNumber(tmp[0], tmp[1], tmp[2]));
        }
        return null;
    }).flatMap(Optional::stream).collect(Collectors.toList());

对于这个流函数,如果全部匹配是,我的意思是如果方法里面的所有函数都会上升。falsemap()NullPointException

如何使此流不产生异常,当所有元素都返回空列表或?nullnull

java nullpointerexception null java-stream 选项类型

评论

2赞 Alexander Ivanchenko 7/1/2022
您能否解释一下逻辑:生成一个流(不是可选的,因为您要返回),并且它后面跟着 ,这意味着流元素应为可选类型?map()VersionNumbernew VersionNumber()flatMap(Optional::stream)
0赞 Alexander Ivanchenko 7/1/2022
如果映射中的函数返回,则如果条件不是肉,则不应返回 - 而是返回一个空的可选函数。Optional<VersionNumber>
0赞 Andy Thomas 7/1/2022
如果您消除了您未提供的 Optional 的期望,那么这个问题将解决如何从流中过滤 null - stackoverflow.com/questions/17081063flatMap(Optional::stream)

答:

3赞 azro 7/1/2022 #1

您可以之后进行筛选

versions.stream().map(vs->{
        if(vs.matches("^matched string$")) {
                ...
                return new VersionNumber(tmp[0], tmp[1], tmp[2]));
        }
        return null;
    }).filter(Objects::nonNull).flatMap(Optional::stream).collect(Collectors.toList());

但越早过滤越明智

versions.stream().filter(vs -> vs.matches("^matched string$"))
                 .map(vs->{
                           ...
                           return new VersionNumber(tmp[0], tmp[1], tmp[2]));
    }).flatMap(Optional::stream).collect(Collectors.toList());

评论

0赞 Andy Thomas 7/1/2022
这在 VersionNumbers 和 null 上效果不佳。flatMap(Optional::stream)
0赞 Hulk 7/1/2022 #2

从 java 16 开始,还有 Stream.mapMulti

返回一个流,该流包含将此流的每个元素替换为多个元素(特别是个或多个元素)的结果。

(强调我的)

使用这个,你的例子就变成了:

versions.stream().mapMulti((vs, consumer)->{
        if(vs.matches("^matched string$")) {
                ...
                consumer.accept(new VersionNumber(tmp[0], tmp[1], tmp[2]));
        }    
}).collect(Collectors.toList());

请注意,null 值根本不会在下游发出,因此不需要筛选或中间流进行平面映射。


另一个例子:

record A(String name, long count) {}

public static void main(String[] args) {
    Stream.of(new A("apple", 1), new A("orange", 3), new A("banana", 0))
        .mapMulti((bucket, consumer) -> {
            for (int i = 0; i < bucket.count(); i++) {
                consumer.accept(bucket.name());
            }
        })
        .forEachOrdered(System.out::println);       
}

指纹:

apple
orange
orange
orange

3个橙子,但没有香蕉

2赞 Alexander Ivanchenko 7/1/2022 #3

如果所有匹配都是假的,我的意思是如果 .map 方法中的所有函数,它将上升一个NullPointException

由于你得到的是一个,这意味着你的代码可以编译和运行。因此,我假设操作中的“代码”不是您的实际代码,因为我们不能将 of 中的元素视为可选元素。NullPointerExceptionmap()Stream<VersionNumber>

flatMap(Optional::stream)仅当 preceding 生成 类型的结果时,才不会导致编译错误。map()Optional<VersionNumber>

因此,我的建议很简单:永远不要返回来代替可选。当可选对象可能是 时,这是完全错误的,它应该包含一个值或为空,但它永远不应该是。nullnullnull

正如 @Andy Thomas 在评论中指出的那样,如果里面的代码真的像 .在这种情况下,用 an 包装 的结果将是对 optional 的误用。返回 instance 或 。然后应用过滤器:Optionalmap()new VersionNumber(tmp[0], tmp[1], tmp[2]));map()OptionalVersionNumbernull

    .map(vs -> {
            if (vs.matches("^matched string$")) {
            ...
            return new VersionNumber(tmp[0], tmp[1], tmp[2]);
        }
        return null;
    })
    .filter(Objects::nonNull)
    .collect(Collectors.toList());

只有当在地图中您实际上没有像您所显示的那样创建时,您才必须求助于可选的使用,但是,例如,进行 API 调用,如果某些条件是肉,则返回 a。VersionNumberOptional<VersionNumber>

    .map(vs -> {
        if (vs.matches("^matched string$")) {
            ...
            return getVersionNumber(tmp[0], tmp[1], tmp[2]); // method returing Optional<VersionNumber>
        }
        return Optional.empty();
    })
    .flatMap(Optional::stream)
    .collect(Collectors.toList());

在这种情况下,无需应用筛选。 当在 empty optional 上调用时,会生成一个空流。Optional.stream()

评论

2赞 Andy Thomas 7/1/2022
或者,更简单地说,返回 null,但不要使用 .flatMap(Optional::stream)
0赞 Alexander Ivanchenko 7/1/2022
@AndyThomas 好点子。改进了答案。