为了账号安全,请及时绑定邮箱和手机立即绑定

在Stream :: flatMap中使用Java 8的Optional

在Stream :: flatMap中使用Java 8的Optional

森栏 2019-09-19 16:23:09
新的Java 8流框架和朋友们制作了一些非常简洁的Java代码,但是我遇到了一个看似简单的情况,简单易懂。考虑一个List<Thing> things方法Optional<Other> resolve(Thing thing)。我想将Things 映射到Optional<Other>s并获得第一个Other。显而易见的解决方案是使用things.stream().flatMap(this::resolve).findFirst(),但flatMap要求您返回一个流,并且Optional没有stream()方法(或者它是Collection一个方法或提供将其转换为或以其方式查看的方法Collection)。我能想到的最好的是:things.stream()    .map(this::resolve)    .filter(Optional::isPresent)    .map(Optional::get)    .findFirst();但这似乎是一个非常普遍的案例,似乎非常冗长。谁有更好的主意?
查看完整描述

3 回答

?
白板的微信

TA贡献1883条经验 获得超3个赞

Java 9

Optional.stream 已添加到JDK 9.这使您可以执行以下操作,而无需任何帮助方法:


Optional<Other> result =

    things.stream()

          .map(this::resolve)

          .flatMap(Optional::stream)

          .findFirst();

Java 8

是的,这是API中的一个小漏洞,因为将Optional变为零或一个长度的流有点不方便。你可以这样做:


Optional<Other> result =

    things.stream()

          .map(this::resolve)

          .flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())

          .findFirst();

但是在flatMap中使用三元运算符有点麻烦,所以编写一个小辅助函数来执行此操作可能会更好:


/**

 * Turns an Optional<T> into a Stream<T> of length zero or one depending upon

 * whether a value is present.

 */

static <T> Stream<T> streamopt(Optional<T> opt) {

    if (opt.isPresent())

        return Stream.of(opt.get());

    else

        return Stream.empty();

}


Optional<Other> result =

    things.stream()

          .flatMap(t -> streamopt(resolve(t)))

          .findFirst();

在这里,我已经内联调用resolve()而不是单独的map()操作,但这是一个品味问题。


查看完整回答
反对 回复 2019-09-19
?
慕沐林林

TA贡献2016条经验 获得超9个赞

你不能像你已经做的那样更简洁。


你声称自己不想.filter(Optional::isPresent) 和 .map(Optional::get)。


这已经通过@StuartMarks描述的方法解决了,但是结果你现在将它映射到一个Optional<T>,所以现在你需要使用.flatMap(this::streamopt)和a get()到底。


所以它仍然包含两个语句,现在您可以使用新方法获得异常!因为,如果每个可选项都是空的怎么办?然后findFirst()将返回一个空的可选项,你的get()意志将失败!


那么你有什么:


things.stream()

    .map(this::resolve)

    .filter(Optional::isPresent)

    .map(Optional::get)

    .findFirst();

是真正实现你想要什么是最好的办法,那就是你要的结果保存为一个T,而不是一个Optional<T>。


我冒昧地创建了一个CustomOptional<T>包装的类,Optional<T>并提供了一个额外的方法,flatStream()。请注意,您无法扩展Optional<T>:


class CustomOptional<T> {

    private final Optional<T> optional;


    private CustomOptional() {

        this.optional = Optional.empty();

    }


    private CustomOptional(final T value) {

        this.optional = Optional.of(value);

    }


    private CustomOptional(final Optional<T> optional) {

        this.optional = optional;

    }


    public Optional<T> getOptional() {

        return optional;

    }


    public static <T> CustomOptional<T> empty() {

        return new CustomOptional<>();

    }


    public static <T> CustomOptional<T> of(final T value) {

        return new CustomOptional<>(value);

    }


    public static <T> CustomOptional<T> ofNullable(final T value) {

        return (value == null) ? empty() : of(value);

    }


    public T get() {

        return optional.get();

    }


    public boolean isPresent() {

        return optional.isPresent();

    }


    public void ifPresent(final Consumer<? super T> consumer) {

        optional.ifPresent(consumer);

    }


    public CustomOptional<T> filter(final Predicate<? super T> predicate) {

        return new CustomOptional<>(optional.filter(predicate));

    }


    public <U> CustomOptional<U> map(final Function<? super T, ? extends U> mapper) {

        return new CustomOptional<>(optional.map(mapper));

    }


    public <U> CustomOptional<U> flatMap(final Function<? super T, ? extends CustomOptional<U>> mapper) {

        return new CustomOptional<>(optional.flatMap(mapper.andThen(cu -> cu.getOptional())));

    }


    public T orElse(final T other) {

        return optional.orElse(other);

    }


    public T orElseGet(final Supplier<? extends T> other) {

        return optional.orElseGet(other);

    }


    public <X extends Throwable> T orElseThrow(final Supplier<? extends X> exceptionSuppier) throws X {

        return optional.orElseThrow(exceptionSuppier);

    }


    public Stream<T> flatStream() {

        if (!optional.isPresent()) {

            return Stream.empty();

        }

        return Stream.of(get());

    }


    public T getTOrNull() {

        if (!optional.isPresent()) {

            return null;

        }

        return get();

    }


    @Override

    public boolean equals(final Object obj) {

        return optional.equals(obj);

    }


    @Override

    public int hashCode() {

        return optional.hashCode();

    }


    @Override

    public String toString() {

        return optional.toString();

    }

}

你会看到我添加了flatStream(),如下所示:


public Stream<T> flatStream() {

    if (!optional.isPresent()) {

        return Stream.empty();

    }

    return Stream.of(get());

}

用作:


String result = Stream.of("a", "b", "c", "de", "fg", "hij")

        .map(this::resolve)

        .flatMap(CustomOptional::flatStream)

        .findFirst()

        .get();

你还需要返回Stream<T>这里,因为你不能返回T,因为如果!optional.isPresent(),那么T == null如果你声明它这样的,但那么你.flatMap(CustomOptional::flatStream)会尝试添加null到流,这是不可能的。


例如:


public T getTOrNull() {

    if (!optional.isPresent()) {

        return null;

    }

    return get();

}

用作:


String result = Stream.of("a", "b", "c", "de", "fg", "hij")

        .map(this::resolve)

        .map(CustomOptional::getTOrNull)

        .findFirst()

        .get();

现在将抛出一个NullPointerException内部流操作。


结论

您使用的方法实际上是最好的方法。


查看完整回答
反对 回复 2019-09-19
  • 3 回答
  • 0 关注
  • 1304 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信