- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
过滤和映射流的默认“最佳实践”是
Stream<T> source;
// ...
Predicate<T> predicate; // = ...
Function<T, U> mapper; // = ...
Stream<U> dst = source
.filter(predicate)
.map(mapper);
在许多软件项目中,您会遇到必须在多个流上应用相同的过滤器和映射操作的情况。例如,T 类对象的集合应该转换为 U 类对象的列表,其中 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
来处理包含 Optionals 的流:
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 原则的情况下处理这些经常使用的过滤和映射习语,您有什么建议?
最佳答案
你可以使用一个收集器(因为你总是以任何方式收集)来过滤某个类的实例:
static <T, U extends T> Collector<T, ?, List<U>> onlyInstancesOfCollector(Class<U> clazz) {
return Collector.of(
ArrayList::new,
(acc, e) -> {
if(clazz.isInstance(e)) {
acc.add(clazz.cast(e));
}
},
(a, b) -> {
a.addAll(b);
return a;
});
}
...
List<U> dst = source.stream()
.collect(onlyInstancesOfCollector(U.class));
哪个具有更好的性能特点:
Benchmark Mode Cnt Score Error Units
Tests.collector avgt 10 0.171 ± 0.003 s/op
Tests.filterAndMap avgt 10 0.203 ± 0.005 s/op
Tests.flatmap avgt 10 0.375 ± 0.012 s/op
完整的 jmh 基准测试:
@BenchmarkMode({ Mode.AverageTime })
@Warmup(iterations = 25)
@Measurement(iterations = 10)
@State(Scope.Benchmark)
public class Tests {
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Tests.class.getSimpleName())
.build();
new Runner(opt).run();
}
List<A> input;
@Setup
public void setup() {
Random r = new Random();
input = new ArrayList<>();
for(int i = 0; i < 10_000_000; i++) {
input.add(r.nextInt(2) == 0 ? new A() : new B());
}
}
@Fork(1)
@Benchmark
public List<B> filterAndMap() {
return input.stream()
.filter(B.class::isInstance)
.map(B.class::cast)
.collect(Collectors.toList());
}
@Fork(1)
@Benchmark
public List<B> flatmap() {
return input.stream()
.flatMap(onlyInstancesOf(B.class))
.collect(Collectors.toList());
}
@Fork(1)
@Benchmark
public List<B> collector() {
return input.stream()
.collect(onlyInstancesOfCollector(B.class));
}
static <T, U> Function<T, Stream<U>> onlyInstancesOf(Class<U> clazz) {
return t -> clazz.isInstance(t)
? Stream.of(clazz.cast(t))
: Stream.empty();
}
static <T, U extends T> Collector<T, ?, List<U>> onlyInstancesOfCollector(Class<U> clazz) {
return Collector.of(
ArrayList::new,
(acc, e) -> {
if(clazz.isInstance(e)) {
acc.add(clazz.cast(e));
}
},
(a, b) -> {
a.addAll(b);
return a;
});
}
}
class A {}
class B extends A {}
关于Java 流 API : Looking for elegant way for filterAndMap,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47906911/
我尝试实现此正则表达式来检查字符串("username")的长度是否在3到30之间,是否仅包含字母(a-z),数字(0-9)和句点(.)(不连续): use regex::Regex; // 1.3.
我又在与正则表达式作斗争了。我一直在尝试添加使用转义字符来转义自定义标记,例如 至 和 至 .在乔治的帮助下,here , 在尝试转义方法之前,以下表达式会产生所需的结果。 ('This is a
我试图根据Python中的正常语法规则正确地分割句子。 我要拆分的句子是 s = """Mr. Smith bought cheapsite.com for 1.5 million dollars,
我有以下将字符串与模式匹配的正则表达式: (?i)(?
我想使用正则表达式过滤掉不使用“\r\n”作为换行符的文件。例如,如果文件包含单个“\n”或单个“\r”,则将被过滤为不合格文件。 我已经找到了: new Regex(@"(?
我正在尝试编写一个正则表达式替换模式,以便替换散列中的数字,如下所示: regexr link some_dict = { TEST: 123 } 这样就可以捕获和替换 123 个。(? " T
因此,我正在使用此查询查询Logstash,该查询返回堆栈顶部的所有内容: { "query": { "match_all": { } }, "size": 7, "_source": { "incl
我正在为 Android 开发 html/javascript 游戏。这是一款棋盘游戏,具有以下功能。它具有不同颜色的图 block ,用户可以在板上放置一个图 block (以编程方式选择)。如果我
所以我目前正在尝试创建一个函数,它将采用两个 3D 点 A 和 B,并为我提供代表 A 点“观察”B 点所需的旋转的四元数(这样点 A 的局部 Z轴穿过点 B,如果你愿意的话)。 我最初找到了this
比如: 第一个数字是:1。 看着第一个数字你可以说1个1,那么第二个数字就是:11。 看着第二个数字你可以说2个1,即第三个数字是:21。 看着第三个
基本上,要点在主题中。 当我创建两个具有固定高度 (2px) 的相同 div,并将缩放比例更改为 75% 或 125% 时,由于某种原因它们“看起来”不同,有人可以解释一下这里发生了什么吗?我该如何解
我正在使用 chrome://inspect/#devices通过cordova检查我的android-app构建的WebView。它在我的 Mac 上运行模拟器。可以找到该设备,但如果我在 WebV
如何使 Angular 服务代码“看起来同步”? 当我清理 Controller 并将业务逻辑代码放入服务中时,我的问题出现了。到目前为止,一切都很好。现在我想在服务函数中“等待”,直到所有异步调用都
标准的 Delphi 控件(面板、按钮等)都有这种斜角效果(顶部和左侧的白线),这给它们带来了 3D 感觉,但今天这让它们看起来很老式。 有没有办法至少在 Delphi 7 中删除这种“3D 外观”?
我有一个在 Win7 上使用 JFileChooser 的 java 应用程序。奇怪的是,有时(经常)但并非总是如此 - 驱动器名称在“查找范围:”组合框中看起来很奇怪: 有没有人知道是什么原因造成的
正在处理一些2015 AoC学习 clojure 的问题...下面的代码对于第 40 次迭代来说足够快,但在那之后很长时间就陷入了停滞。我与其他一些人的解决方案进行了比较,但我并不清楚为什么这么慢。我
我有一个在 Win7 上使用 JFileChooser 的 java 应用程序。奇怪的是,有时(经常)但并非总是如此 - 驱动器名称在“查找范围:”组合框中看起来很奇怪: 有没有人知道是什么原因造成的
我从 JUnit 开始,尝试找到测试的最佳断言方法。 假设我有一个函数来测试它 不返回值 如果输入无效则抛出异常 例如 void foo (int a) throws Exception { if
为了我的优化,我想在 Rcpp 中获得一个像样的 toupper。我对 C++ 很陌生,据我所知,我已经做到了: #include using namespace Rcpp; void C_toup
我在这里不知所措。我有一个简单的地形生成算法在工作,并且通过扩展 ViewPlatformAWTBehavior 和处理我自己的事件,我有一些简单的键盘导航在工作。一切都很好,我可以跟随地形。万岁!
我是一名优秀的程序员,十分优秀!