JavaConversions对象提供了用于在Scala和Java集合之间来回转换的一组方法。
给目标值显式地指定一个类型来触发转换 例如:
import scala col lec ti JavaConversions._
val props: scala.collection.mutable.Map[String, String] = System .getProperties()
如果你担心那些不需要的隐式转换也被引人的话,只引入需要的即可 例如:
import scala.collection.JavaConversions.propertiesAsScalaMap
注意这些转换产出的是包装器,让你可以使用目标接口来访问原本的值。举例来说,如果你用
val props: scala.collection.mutable.Map[String, String] = System.getProperties()
那么props就是一个包装器,其方法将调用底层的Java对象。如果你调用
props ("com.horstmann.scala")= "impatient"
那么包装器将调用底层Properties对象的put("com.horstmann.scala", "impatient")
。
从Scala集合到Java集合的转换:
隐式函数 | 从scala.collection的类型 | 到java.util的类型 |
asJavaCollection | Iterable | Collection |
asJavaIterable | Iterable | Iterable |
asJavaIterator | Iterator | Iterator |
asJavaEnumeration | Iterator | Enumeration |
seqAsJavaList | Seq | List |
mutableSeqAsJavaList | mutable.Seq | List |
bufferAsJavaList | mutable.Buffer | List |
setAsJavaSet | Set | Set |
mutableSetAsJavaSet | mutable.Set | Set |
mapAsJavaMap | Map | Map |
mutableMapAsJavaMap | mutable.Map | Map |
asJavaDictionary | Map | Dictionary |
asJavaConcurrentMap | mutable.ConcurrentMap | concurrent.ConcurrentMap |
从Java集合到Scala集合的转换:
隐式函数 | 从java.util的类型 | 到scala.collection的类型 |
collectionAsScalaIterable | Collection | Iterable |
iterableAsScalaIterable | Iterable | Iterable |
asScalaIterator | Iterator | Iterator |
enumerationAsScalaIterator | Enumeration | Iterator |
asScalaBuffer | List | mutable.Buffer |
asScalaSet | Set | mutable.Set |
mapAsScalaMap | Map | mutable.Map |
dictionaryAsScalaMap | Dictionary | mutable.Map |
propertiesAsScalaMap | Properties | mutable.Map |
asScalaConcurrentMap | concurrent.ConcurrentMap | mutable.ConcurrentMap |
并行集合
Scala提供的用于操纵大型集合的解决方案十分诱人。这些任务通常可以很自然地并行操作。举例来说,要计算所有元素之和,多个线程可以并发地计算不同分区的和;最后这些部分的结果被汇总到一起。要对这样的并发任务进行排程是很伤脑筋的,但若用Scala ,则你无须担心这个问题。如果coll是个大型集合,那么
val coll = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
println(coll.par.sum)
上述代码会并发地对它求和。par方法产出当前集合的一个并行实现。该实现会尽可能并行地执行集合方法。例如:
coll.par.count(_ % 2 == 0)
将会并行地对偶集合求前提表达式的值,然后将结果组合在一起,得出coll中所有偶数的数量。
你可以通过对要遍历的集合应用.par并行化for循环
,就像这样:
for (i <- ((0 until 10)).par) {
print(s"$i ")
}
// 输出: 7 9 3 2 1 8 5 4 0 6
数字是按照作用于该任务的线程产出的顺序输出的。而在for/yield循环中,结果是依次组装的。如下:
(for(i<- (0 until 100000).par) yield i) == (0 until 100000)
注意:如果并行运算修改了共享的变量,则结果无法预知。举例来说, 不要更新个共享的计数器:
var count = 0
for (c <- coll.par) {
if (c % 2 == 0) {
count += 1
}
}
par方法返回的并行集合属于扩展自ParSeq、ParSet或ParMap特质的类型。这些并不是Seq、Set、Map 的子类型,你不能向一个预期顺序集合( sequential collection ) 的方法传入并行集合。
你可以用seq方法将并行集合转换回顺序集合:
val result= coll.par.filter(_ % 2 == 0).seq
并非所有方法都能被并行化。例如,reduceLeft和reduceRight要求每个操作符都要按顺序应用。有另一个方法reduce,对集合的部分进行操作然后组合出结果。为了让这个方案可行,操作符必须是结合的( associative ),它必须满足的要求。例如,加法是结合的而减法不是:
。
同理,还有一个fold方法对集合的部分进行操作。可惜它并不像foldLeft和foldRight那么灵活,操作符的两个参数都必须是元素。也就是说,你可以执行coll.par.fold(0)(_ + _),但不能执行更复杂的折叠。
要解决这个问题,有个更一般的aggregate方法,将操作符应用到集合的部分,然后用另一个操作符来组合出结果。例如,str.par.aggregate(Set[Char]())(_ + _, _ ++ _)
等效于str.foldLeft(Set[Char]())(_ + _)
,得到str中所有不同字符的集。
说明: 默认情况下,并行集合使用全局的fork-join线程池,该线程池非常适用于高处理器开销的计算。如果你执行的并行步骤包含阻塞调用,就应该另选一种“执行上下文(execution context)”。