过滤和映射流的默认“最佳实践”是
Stream<T> source;
// ...
Predicate<T> predicate; // = ...
Function<T, U> mapper; // = ...
Stream<U> dst = source
.filter(predicate)
.map(mapper);
在许多软件项目中,你会遇到同样的过滤和映射操作需要应用于多个流的情况。例如,一个T类对象的集合应该被转换为U类对象的List,其中U是T的子类,并且我们只想要U的实例。因此,可以这样写:
Collection<T> source;
// ...
List<U> dst = source.stream()
.filter(U.class::isInstance)
.map(U.class::cast)
.collect(Collectors.toList());
为了概括这个内容,我编写了一个帮助方法,称为
onlyInstancesOf
:static <T, U> Function<T, Stream<U>> onlyInstancesOf(Class<U> clazz) {
return t -> clazz.isInstance(t)
? Stream.of(clazz.cast(t))
: Stream.empty();
}
这个方法旨在和 flatMap
一起使用:
List<U> dst = source.stream()
.flatMap(onlyInstancesOf(U.class))
.collect(Collectors.toList());
我经常使用的另一个函数是optionalPresent
,用于处理包含可选值的流:
static <T> Function<Optional<T>, Stream<T>> optionalPresent() {
return t -> t.map(Stream::of).orElse(Stream.empty());
}
使用方法:
Collection<Optional<T>> source;
// ...
List<T> dst = source.stream()
.flatMap(optionalPresent())
.collect(Collectors.toList());
这些解决方案看起来很优雅,但是它们有一个很大的缺点:它们比先过滤再映射的“传统”解决方案慢了10倍以上。
您会建议如何处理这些经常使用的过滤和映射习惯用语而不违反DRY原则?
stream.flatMap(Optional::stream)
来处理Optional
类型的情况。 - marstranonlyInstancesOf
只接受一个输入而不是该类的Stream
? - Eugenestream.flatMap(onlyInstancesOf(U.class))
比stream.filter(U.class::isInstance).map(U.class::cast)
慢大约10倍(至少我用一个小程序测量了这个结果,该程序创建了1000万个类T的实例,在随机情况下每秒钟创建一次类U的实例,然后收集所有类U的对象)。 - Torsten Fehreflatmap
版本的速度大约慢了1.8倍。你有读过https://dev59.com/hHRB5IYBdhLWcg3wz6UK吗? - Jorn Vernee