java lambda - 如何遍历可选列表/可选流

Posted

技术标签:

【中文标题】java lambda - 如何遍历可选列表/可选流【英文标题】:java lambda - how to traverse optional list/stream of optionals 【发布时间】:2019-01-11 02:07:42 【问题描述】:

有一个 Optional 的可选列表:

Optional<List<Optional<String>>> optionalList = Optional.of(
    Arrays.asList(
        Optional.empty(),
        Optional.of("ONE"),
        Optional.of("TWO")));

如何遍历optionalList打印出字符串的ONETWO

如果有一个可选的可选流呢?

Optional<Stream<Optional<String>>> optionalStream = Optional.of(
    Stream.of(
        Optional.empty(),
        Optional.of("ONE"),
        Optional.of("TWO")));

更新:感谢您的回答, optionalStream 的解决方案(非嵌套):

optionalStream
    .orElseGet(Stream::empty)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .forEach(System.out::println);

【问题讨论】:

到目前为止你有什么尝试? 您也可以只将Optional 映射到Stream,而不是检查isPresent 并自己明确获取值。会更“实用风格”。 您应该避免将Optional 存储在集合中,请参阅Uses for Optional 和Is it Worth it to Use 'Optional' in Collections? @DidierL true,但是对于 Stream&lt;Optional&lt;T&gt;&gt; 的情况,这仍然是一个有效的练习,当您的集合中的项目通过其他一些函数映射到 Optional&lt;T&gt; 时,这种练习更为常见,所以你最终得到一个Stream&lt;Optional&lt;T&gt;&gt;,然后你想要过滤并转换为Stream&lt;T&gt; 确实,对我来说这也是一个有效的案例。请注意,Java 9 中引入了 Optional.stream() 方法来避免这种情况(与 flatMap() 结合使用)。最后,我也会避免使用Optional&lt;List&gt;Optional&lt;Stream&gt;,因为它们不太友好。尽可能使用空列表/流,因为这会使 API 更加简洁。 【参考方案1】:

首先,检查Optional 是否存在。如果是,则流式传输列表并过滤非空列表并打印每个列表。

optionalList.ifPresent(list -> list.stream()
            .filter(Optional::isPresent)
            .map(Optional::get)
            .forEach(System.out::println));

流的情况也差不多

optionalStream.ifPresent(stream -> stream
            .filter(Optional::isPresent)
            .map(Optional::get)
            .forEach(System.out::println));

【讨论】:

添加.map(Optional::get) 将打印字符串【参考方案2】:

如果你可以使用Java 9,可以这样做:

optionalList.ifPresent(list -> list.stream()
  .flatMap(Optional::stream)
  .forEach(System.out::println));

对于一个可选流,它是相同的,没有第一个 .stream() 调用。

在 Java 8 中,您没有可用的 Optional::stream 方法,因此您可以自己做:

optionalList.ifPresent(list -> list.stream()
  .flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
  .forEach(System.out::println));

对于Optionals 中的Stream,它看起来像这样:

optionalStream.ifPresent(stream -> stream
  .flatMap(opt -> opt.map(Stream::of).orElseGet(Stream::empty))
  .forEach(System.out::println));

【讨论】:

尝试了省略第一个 .stream() 调用的选项流 - 但不起作用(java 8) 你是什么意思不起作用?刚试了Java 8版本,第一行替换为:optionalStream.ifPresent(stream -&gt; stream肯定可以。【参考方案3】:

您确实可以流式传输Option&lt;String&gt; 并仅过滤非空值。

Optional<List<Optional<String>>> optionalList = Optional.of(Arrays.asList(Optional.empty(), Optional.of("ONE"), Optional.of("TWO")));

optionalList.orElseGet(ArrayList::new)
            .stream()
            .filter(Optional::isPresent)
            .map(Optional::get)           
            .forEach(System.out::println);

您也可以按照其他答案中的建议使用Optional.ifPresent()

optionalList.ifPresent(l -> l.stream()
                             .filter(Optional::isPresent)
                             .map(Optional::get)                               
                             .forEach(System.out::println));

我个人更喜欢第一种方式,因为它消除了嵌套级别:我觉得阅读起来更愉快。

【讨论】:

如果列表不存在,第一种方法将创建一个空的ArrayList 没错。但是在流执行范围内创建一个空的ArrayList 也没有任何成本。 我手边没有 IDE,但flatMap 不会满足所有isPresent 检查的需求吗? @chrylis 是的。所有这些答案都太臃肿了,只要将列表中的Optional 项目转换为StreamflatMap 即可。【参考方案4】:

我看到有两种方法,第二种对我来说看起来更漂亮,看看:

class Scratch 
    public static void main(String[] args) 
        Optional<String> element1 = Optional.of("test1");
        Optional<String> element2 = Optional.empty();
        Optional<String> element3 = Optional.of("test2");
        Optional<String> element4 = Optional.of("test3");
        List<Optional<String>> list = Arrays.asList(element1, element2, element3, element4);

        System.out.println(extractStrings1(list));
        System.out.println(extractStrings2(list));
    

    private static List<String> extractStrings1(List<Optional<String>> list) 
        return list.stream()
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());
    

    private static List<String> extractStrings2(List<Optional<String>> list) 
        List<String> result = new ArrayList<>();
        list.forEach(element -> element.ifPresent(result::add));
        return result;
    

【讨论】:

【参考方案5】:

嗯……

    检查是否存在可选列表。 对(现在存在的)列表的所有元素执行“for each”。 在每个步骤中检查是否存在可选字符串。 如果是,请打印。

单线可以做到这一点:

optionalList.ifPresent(list -> list.forEach(s -> s.ifPresent(System.out::println)));

【讨论】:

【参考方案6】:
optionalList.stream().flatMap(List::stream).filter(Objects::nonNull).forEach(...)

【讨论】:

以上是关于java lambda - 如何遍历可选列表/可选流的主要内容,如果未能解决你的问题,请参考以下文章

遍历可选的整数列表 ifPresent

01期Lambda表达式及函数式接口原理

kotlin lambda 表达式作为可选参数

如何使用 Java lambda 遍历列表映射并返回单个值? [复制]

java lambda

如何在可选类型中使用 numpy