假设我有一个应该转换为Map的集合,但不是像map方法那样一对一的方式。
var map = collection.mutable.HashMap()
for (p <- dataList.par) {
if(cond1(p)) {
map += (p, true)
} else {
// do nothing
}
}
我想出了几个解决方案,想知道什么是最好的。
map.synchronize { map += (p, true) }
使用actor更新地图。但我不知道如何等待所有演员任务完成
yield Some(p) or None
然后运行foreach { case Some(p) => map += (p, true)}
。但是,如果第一个迭代器来自并行集合,我不知道如何使它成为顺序。答案 0 :(得分:5)
不确定实际上是否会表现最佳,但这应该使条件的评估平行:
import scala.collection._
val map: mutable.Map[Int, Boolean]
= dataList.par.collect{case p if cond1(p) => (p, true)}(breakOut)
(使用可变映射,因为它是您的代码所做的,但这不是必需的。)
上下文必须为: mutable.Map[Int, Boolean]
提供预期结果的类型(因此breakOut
)才能生效。
修改: breakOut
为scala.collection.breakOut
。返回集合的集合操作(此处为collect
)采用隐式参数bf: CanBuildFrom[SourceCollectionType, ElementType, ResultType]
。库提供了隐式CanBuildFroms
,以便返回最佳的ResultType,最好的方法是最接近源集合类型。传递breakOut代替此隐式参数,以便可以选择另一个CanBuildFrom
,因此可以选择结果类型。 breakOut
做的是选择CanBuildFrom
,而不考虑源集合类型。但是,有许多可用的含义,没有优先权规则。这就是为什么结果类型必须由上下文给出,以便可以选择其中一个含义。
总而言之,当传递breakOut
代替隐式参数时,结果将构建为上下文中预期的类型。
答案 1 :(得分:5)
另一个答案中提到的breakOut
解析为构建器工厂,用于收集预期类型map
。预期的map
类型为mutable.Map[Int, Boolean]
。
由于构建器工厂由顺序集合提供,collect
不会并行进行:
scala> val cond1: Int => Boolean = _ % 2 == 0
cond1: Int => Boolean = <function1>
scala> val dataList = 1 to 10
dataList: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val map: mutable.Map[Int,Boolean] = dataList.par.collect{case p if cond1(p) => println(Thread.currentThread); (p, true)}(breakOut)
Thread[Thread-8,5,main]
Thread[Thread-8,5,main]
Thread[Thread-8,5,main]
Thread[Thread-8,5,main]
Thread[Thread-8,5,main]
map: scala.collection.mutable.Map[Int,Boolean] = Map(10 -> true, 8 -> true, 4 -> true, 6 -> true, 2 -> true)
您可以从线程名称中看到 - 线程应包含名称ForkJoin
- 。
正确的方法应该是首先使用期望类型为并行映射的breakOut
,以便collect
并行进行:
scala> val map: parallel.mutable.ParMap[Int,Boolean] = dataList.par.collect{case p if cond1(p) => println(Thread.currentThread);(p, true)}(breakOut)
Thread[Thread-9,5,main]
Thread[Thread-9,5,main]
Thread[Thread-9,5,main]
Thread[Thread-9,5,main]
Thread[Thread-9,5,main]
map: scala.collection.parallel.mutable.ParMap[Int,Boolean] = ParHashMap(10 -> true, 8 -> true, 4 -> true, 6 -> true, 2 -> true)
然后针对seq
的结果致电collect
,因为seq
始终为O(1)
。
更新:刚刚检查过 - 这似乎与trunk一起正常工作,但与2.9.1.final无关。
但是,正如您所看到的,这不起作用,因为它是一个错误,并将在下一版本的Scala中修复。解决方法:
scala> val map: parallel.mutable.ParMap[Int, Boolean] = dataList.par.collect{case p if cond1(p) => println(Thread.currentThread);(p, true)}.map(x => x)(breakOut)
Thread[ForkJoinPool-1-worker-7,5,main]
Thread[ForkJoinPool-1-worker-3,5,main]
Thread[ForkJoinPool-1-worker-0,5,main]
Thread[ForkJoinPool-1-worker-8,5,main]
Thread[ForkJoinPool-1-worker-1,5,main]
map: scala.collection.parallel.mutable.ParMap[Int,Boolean] = ParHashMap(10 -> true, 8 -> true, 4 -> true, 6 -> true, 2 -> true)
scala> val sqmap = map.seq
sqmap: scala.collection.mutable.Map[Int,Boolean] = Map(10 -> true, 8 -> true, 4 -> true, 6 -> true, 2 -> true)
请注意,最终map
目前将按顺序完成。
或者,如果只有parallel.ParMap
可以,您可以这样做:
scala> val map: Map[Int, Boolean] = dataList.par.collect{case p if cond1(p) => println(Thread.currentThread);(p, true)}.toMap.seq
Thread[ForkJoinPool-1-worker-2,5,main]
Thread[ForkJoinPool-1-worker-3,5,main]
Thread[ForkJoinPool-1-worker-7,5,main]
Thread[ForkJoinPool-1-worker-1,5,main]
Thread[ForkJoinPool-1-worker-8,5,main]
map: scala.collection.Map[Int,Boolean] = Map(10 -> true, 6 -> true, 2 -> true, 8 -> true, 4 -> true)