有什么理由可以选择filter+map
:
list.filter (i => aCondition(i)).map(i => fun(i))
Run Code Online (Sandbox Code Playgroud)
结束collect
?:
list.collect(case i if aCondition(i) => fun(i))
Run Code Online (Sandbox Code Playgroud)
与一个collect
(单看)看上去更快和更清洁的我.所以我会一直这样做collect
.
Rex*_*err 43
Scala的大多数集合急切地应用操作,并且(除非您使用的宏程序库为您执行此操作)不会融合操作.因此,filter
随后map
通常会创建两个集合(即使你使用Iterator
或诸如此类的中间形式将瞬时产生,虽然只是在时间的元素),而collect
不会.
另一方面,collect
使用部分函数来实现联合测试,并且部分函数A => Boolean
在测试集合中是否有东西时比谓词()慢.
此外,有些情况下,读取其中一个比另一个更清晰,并且您不关心性能或内存使用差异大约2倍左右.在这种情况下,使用更清楚的.通常,如果您已经拥有了名为的函数,则可以更清楚地阅读
xs.filter(p).map(f)
xs.collect{ case x if p(x) => f(x) }
Run Code Online (Sandbox Code Playgroud)
但如果您在线提供封闭,collect
通常看起来更干净
xs.filter(x < foo(x, x)).map(x => bar(x, x))
xs.collect{ case x if foo(x, x) => bar(x, x) }
Run Code Online (Sandbox Code Playgroud)
即使它不一定更短,因为你只引用变量一次.
现在,性能差异有多大?这有所不同,但如果我们考虑这样的集合:
val v = Vector.tabulate(10000)(i => ((i%100).toString, (i%7).toString))
Run Code Online (Sandbox Code Playgroud)
并且你想要根据过滤第一个条目来选择第二个条目(因此过滤器和地图操作都非常简单),然后我们得到下表.
注意:可以在集合中获取惰性视图并在那里收集操作.您并不总是返回原始类型,但您始终可以使用to
获取正确的集合类型.因此xs.view.filter(p).map(f).toVector
,因为视图,不会创建中间件.这也在下面进行测试.人们还建议人们可以xs.flatMap(x => if (p(x)) Some(f(x)) else None)
并且这是有效的. 事实并非如此. 它也在下面测试过.并且可以通过显式创建构建器来避免部分功能: val vb = Vector.newBuilder[String]; xs.foreach(x => if (p(x)) vb += f(x)); vb.result
,其结果也列在下面.
在下表中,测试了三个条件:过滤掉任何内容,过滤掉一半,过滤掉所有内容.时间已经标准化为过滤/映射(100%=与过滤器/地图相同的时间,越低越好).误差范围约为±3%.
不同过滤器/地图替代品的性能
====================== Vector ========================
filter/map collect view filt/map flatMap builder
100% 44% 64% 440% 30% filter out none
100% 60% 76% 605% 42% filter out half
100% 112% 103% 1300% 74% filter out all
Run Code Online (Sandbox Code Playgroud)
因此,filter/map
并且collect
通常非常接近(collect
当你保持很多时获胜),flatMap
在所有情况下都要慢得多,并且创建一个建造者总是赢.(这是特别适用于Vector
.其他集合可能有一些不同的特征,但大多数的趋势将是相似的,因为操作的差异是相似的.)此测试中的视图往往是一个胜利,但它们并不总是无缝地工作(而且它们并不比collect
空案更好).
因此,底线:喜欢filter
那么map
它是否有助于画面的清晰度速度并不重要,或者更喜欢它的速度,当你过滤掉几乎所有的东西,但仍希望保持功能(所以不想使用生成器); 否则使用collect
.
我想这是基于意见的,但给出了以下定义:
scala> val l = List(1,2,3,4)
l: List[Int] = List(1, 2, 3, 4)
scala> def f(x: Int) = 2*x
f: (x: Int)Int
scala> def p(x: Int) = x%2 == 0
p: (x: Int)Boolean
Run Code Online (Sandbox Code Playgroud)
您觉得这两本中哪一本更好读:
l.filter(p).map(f)
Run Code Online (Sandbox Code Playgroud)
或者
l.collect{ case i if p(i) => f(i) }
Run Code Online (Sandbox Code Playgroud)
(请注意,我必须修复上面的语法,因为您需要括号并case
添加if
条件)。
我个人觉得filter
+map
更容易阅读和理解。这完全取决于您使用的确切语法,但是考虑到p
and ,您在使用orf
时不必编写匿名函数,而在使用 Collect 时确实需要它们。filter
map
您还可以使用flatMap
:
l.flatMap(i => if(p(i)) Some(f(i)) else None)
Run Code Online (Sandbox Code Playgroud)
这可能是 3 个解决方案中最有效的,但我发现它不如map
和好filter
。
总的来说,很难说哪一个更快,因为这取决于很多优化最终由scalac
JVM 执行。所有 3 个应该非常接近,并且绝对不是决定使用哪一个的因素。
归档时间: |
|
查看次数: |
8434 次 |
最近记录: |