Java流是否有一种方便的方法可以基于谓词进行映射,但如果不满足谓词,则可以映射到其他值?
假设我有Stream.of("2021", "", "2023")
。我想把它映射到Stream.of(Optional.of(Year.of(2021)), Optional.empty(), Optional.of(Year.of(2023)))
。我可以这样做:
Stream<String> yearStrings = Stream.of("2021", "", "2023");
Stream<Optional<Year>> yearsFound = yearStrings.map(yearString ->
!yearString.isEmpty() ? Year.parse(yearString) : null)
.map(Optional::ofNullable);
但我想做的是,使用一个假设的过滤器映射:
Stream<String> yearStrings = Stream.of("2021", "", "2023");
Stream<Optional<Year>> yearsFound = yearStrings.mapIfOrElse(not(String::isEmpty),
Year::parse, null).map(Optional::ofNullable);
当然,我可以编写自己的mapIfOrElse(Predicate<>, Function<>, T)
函数与Stream.map()
一起使用,但我想检查一下Java现有的武器库中是否有类似的东西我遗漏了。
没有比现有方法更好的方法了-如果将其提取到一个方法中可能会更好,但实际上就是这样。
另一种方法可能是从所有值构造Optionals,然后使用Optional.filter
将空值映射到空Optionals:
yearStreams.map(Optional::of)
.map(opt -> opt.filter(Predicate.not(String::isEmpty)));
这更好吗?可能不会。
另一种方法是使用Guava的Strings.emptyToNull
(其他库也可用(,它首先将空字符串转换为null;然后使用Optional.ofNullable
将非null和null分别转换为非空和空Optional
:
yearStreams.map(Strings::emptyToNull)
.map(Optional::ofNullable)
您只需使用filter
进行验证,然后仅使用map
Stream<Year> yearsFound = yearStrings.filter(yearString->!yearString.isEmpty()).map(Year::parse)
很难在一个流操作中以可读的方式平稳地组合所有这些操作。
下面是一个使用Java 16mapMulti()
:链接的奇怪方法
Stream<Optional<Year>> yearsFound = yearStrings
.mapMulti((yearString, consumer) ->
Optional.of(yearString).filter(s -> !s.isEmpty()).map(Year::parse)
.ifPresentOrElse(year -> consumer.accept(Optional.of(year)),
() -> consumer.accept(Optional.empty()))
);