在 Java 中使用 Optional 和 Streams

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24254209/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:06:03  来源:igfitidea点击:

Using Optional with Streams in Java

javajava-8optionaljava-stream

提问by gaijinco

I was trying to refactor an old code to use streams, and my first approach was this:

我试图重构旧代码以使用流,我的第一种方法是:

public void run() throws IOException {
   Files.list(this.source)
        .filter(Images::isImage)
        .map(Image::new)
        .filter(image -> image.isProportional(this.height, this.width))
        .map(image -> image.resize(this.height, this.width))
        .forEach(image -> Images.write(image, this.destination));
}

This is not compiling since new Image() and Images.write() throws IOExceptions.

这不是编译,因为 new Image() 和 Images.write() 抛出 IOExceptions。

Wrapping those exceptions with UncheckedIOException wouldn't do the trick as I don't want to stop other images to be processed if one of them fails.

用 UncheckedIOException 包装这些异常不会解决问题,因为如果其中一个失败,我不想停止处理其他图像。

So I ended writing 2 private methods:

所以我结束了编写 2 个私有方法:

private Optional<Image> createImage(Path imagePath) {
    try {
        return Optional.of(new Image(imagePath));
    } catch (IOException e) {
        return Optional.empty();
    }
}

private void write(Image image) {
    try {
        Images.write(image, this.destination);
    } catch (IOException e) {
        // log error
    }
}

createImage() returns an Optional since this seems sensible. However after this my code got really ugly:

createImage() 返回一个 Optional ,因为这似乎是明智的。但是在此之后我的代码变得非常丑陋:

public void run() throws IOException {
    Files.list(source)
         .filter(Images::isImage)
         .map(this::createImage)
         .filter(image -> image.isPresent() && image.get().isProportional(this.height, this.width))
         .map(image -> image.get().resize(this.height, this.width))
         .forEach(this::write);
}

Is there a way to avoid using get() and isPresent() on that code?

有没有办法避免在该代码上使用 get() 和 isPresent() ?

Thanks!

谢谢!

采纳答案by srborlongan

One of the nice things about Optionals is that applying filtering, mapping and flat-mapping functions on them only trigger when Optional::isPresent is true, so:

Optionals 的好处之一是对它们应用过滤、映射和平面映射函数仅在 Optional::isPresent 为真时触发,所以:

public void run() throws IOException {
    Files.list(source)
         .filter(Images::isImage)
         .map(this::createImage)
         // turns every non-proportional Optional<Image> into empty optionals
         .map(image -> image.filter(i -> i.isProportional(this.height, this.width)))
         // resizes every proportional Optional<Image>, while doing nothing on the empties
         .map(image -> image.map(i -> i.resize(this.height, this.width)))
         // applies the writing consumer for each non-empty Optional<Image>
         .forEach(image -> image.ifPresent(this::write));
}

Another way is to only call Optional::isPresent and Optional::get in separate Stream transformations:

另一种方法是在单独的 Stream 转换中只调用 Optional::isPresent 和 Optional::get:

public void run() throws IOException {
    Files.list(source)
         .filter(Images::isImage)
         .map(this::createImage)
         // filter out the empty optionals
         .filter(Optional::isPresent)
         // replace every optional with its contained value
         .map(Optional::get)
         .filter(image -> image.isProportional(this.height, this.width))
         .map(image -> image.resize(this.height, this.width))
         .forEach(this::write);
}

Yet another way (which I refuse to recommend as a primary solution because of its relative weirdness) is to change the static image creation method into a Stream generator, instead of an Optional generator, to take advantage of flatMap:

还有一种方法(我拒绝推荐作为主要解决方案,因为它相对奇怪)是将静态图像创建方法更改为 Stream 生成器,而不是 Optional 生成器,以利用 flatMap:

private Stream<Image> createImage(Path imagePath) {
    try {
        return Stream.of(new Image(imagePath));
    } catch (IOException e) {
        return Stream.empty();
    }
}

public void run() throws IOException {
    Files.list(source)
         .filter(Images::isImage)
         // inserts into the stream the resulting image (empty streams are handled seamlessly)
         .flatMap(this::createImage)
         .filter(image -> image.isProportional(this.height, this.width))
         .map(image -> image.resize(this.height, this.width))
         .forEach(this::write);
}

On second thought, go with this solution; it seems to be simpler, and since the static method is private anyway, less screaming would occur from end-users, other developers, and random people with access to decent Java 8 decompilers (http://www.benf.org/other/cfr/).

再三考虑,选择这个解决方案;它似乎更简单,而且由于静态方法无论如何都是私有的,因此最终用户、其他开发人员和可以访问体面的 Java 8 反编译器的随机人员(http://www.benf.org/other/ CFR/)。

回答by Ruben

From Java9, you can use flatMapand Optional::streamto filter empty Optionals:

在 Java9 中,您可以使用flatMapOptional::stream来过滤空的 Optionals:

public void run() throws IOException {
    Files.list(source)
         .filter(Images::isImage)
         .map(this::createImage)
         .flatMap(Optional::stream)
         .filter(image -> image.isProportional(this.height, this.width))
         .map(image -> image.resize(this.height, this.width))
         .forEach(this::write);
}