1. 多参数列表
Scala 允许你指明函数的最后一个参数可以是重复的。这可以允许客户向函数传入可变长度参数列表。想要标注一个重复参数,在参数的类型之后加一个 *
。例如:
def echo(args: String*) = {
for (arg <- args) println(arg)
}
这样定义, echo 可以被零个至多个 String 参数调用
echo()
echo("one")
echo("hello", "world")
函数内部,重复参数的类型是声明参数类型的数组。因此, echo 函数里被声明为类型“ String* ”
的 args 的类型实际上是 Array[String] 。然而,如果你有一个合适类型的数组,并尝试把它当作
重复参数传入,你会得到一个编译器错误:
val arr = Array("I`m", "from", "China")
echo(arr)
# 编译报错
error: type mismatch;
found : Array[java.lang.String]
required: String
要实现这个做法,你需要在数组参数后添加 : _*
符号,像这样:
val arr = Array("I`m", "from", "China")
echo(arr: _*)
这个标注 : _*
告诉编译器把 arr 的每个元素当作参数,而不是当作单一的参数传给 echo 。
2. Tuple2与Map
Map(1->"a", 2->"b").map { case (i, j)=>
(1, j)
}
res0: scala.collection.immutable.Map[Int,String] = Map(1 -> b)
如果map中返回的是Tuple2类型, 那么最后结果会转化成Map, 同时相同的key会被覆盖掉
Map(1->"a", 2->"b").map { case (i, j)=>
(1, 1, j)
}
res3: scala.collection.immutable.Iterable[(Int, Int, String)] = List((1,1,a), (1,1,b))
如果map中返回的不是Tuple2类型, 那么最后结果就是List而不是Map, 此时就不必担心数据被覆盖的问题了
3. Java集合与Scala集合相互转换
在 scala.collection.convert
包下定义了常用的一些隐式转换类
import java.util
import scala.collection.convert.ImplicitConversionsToScala._
import scala.collection.convert.ImplicitConversionsToJava._
object CommonImplicit {
implicit def map2ScalaMap[K, V](map: util.Map[K, V]): Map[K, V] = `map AsScala`(map).toMap
implicit def list2ScalaList[T](list: util.List[T]): List[T] = `list asScalaBuffer`(list).toList
implicit def map2JavaMap[K, V](map: Map[K, V]): util.Map[K, V] = `map AsJavaMap`(map)
implicit def map2JavaList[T](list: List[T]): util.List[T] = `seq AsJavaList`(list)
}
在使用时直接 import CommonImplicit._
即可
4. reduce与foldLeft
reduce: 对集合元素进行归约操作, 默认从左到右两两运算, 然后结果与右边的元素继续运算, 最终得到结果
scala> List[Int]().reduce(_ + _)
scala> java.lang.UnsupportedOperationException: empty.reduceLeft
at scala.collection.LinearSeqOptimized.reduceLeft(LinearSeqOptimized.scala:139)
查看源码发现reduce默认调用reduceLeft, 而reduceLeft会判断集合是否为空, 如果是则抛出异常
上图我们可以看到reduceLeft实际调用的是foldLeft, 所以这时候需要用foldLeft来代替reduceLeft
因为集合为空, 所以我们需要给一个初始的head填充到原来的集合中:
scala> List[Int]().foldLeft(0)(_ + _)
等价于
scala> List[Int](0).reduce(_ + _)
5. map合并, k相同v累加
使用 /:
(map1 /: map2) { case (map, (k, v)) => map + (k -> v + map.getOrElse(k, 0))
查看源码
其实是以其中一个map作为基础, 将另一个map的键值对做折叠累加操作
6. Future
Future[T]
是一个容器类型,代表一种返回值类型为 T
的计算。 计算可能会出错,也可能会超时.
object Future {
def apply[T](body: => T)(implicit execctx: ExecutionContext): Future[T]
}
要异步执行的计算通过传名参数 body
传入。 第二个参数是一个隐式参数,隐式参数是说,函数调用时,如果作用域中存在一个匹配的隐式值,就无需显示指定这个参数。ExecutionContext
可以执行一个 Future,可以把它看作是一个线程池,是绝大部分 Future API 的隐式参数。
import scala.concurrent.ExecutionContext.Implicits.global
语句引入了一个全局的执行上下文,确保了隐式值的存在。 这时候,只需要一个单元素列表,可以用大括号来代替小括号。
计算会在 Future 创建后的某个不确定时间点上由ExecutionContext
给其分配的某个线程中执行。
Future回调
Future {
// do something ...
ret
}.onComplete {
case Success(ret) => println(s"计算成功返回 $ret")
case Failure(ex) => println("计算失败, 异常: $ex")
}
也可以单独使用 onSuccess
或 onFailure
分别注册成功/失败回调
Future组合
所有容器类型都可以进行map
、flatMap
操作,也可以用在 for 语句中。 作为一种容器类型,Future 支持这些操作也不足为奇!
map
def cal(i: Int, j: Int): Future[Int] = Future(i+j) val b: Future[Boolean] = cal(1, 2).map(_ > 3)
flatMap
def check(i: Int): Future[Boolean] = Future(i == 5) val b: Future[Boolean] = cal(1, 3).flatMap(i => check(i))
for
上面的flatMap
,也可以写成for
语句for { i <- cal(1, 4) b <- check(i) } yield b val b: Future[Boolean] = a.flatMap(s => check(s))
注意 上述
for
与flatMap
的两种写法, future的创建存在先后顺序, 并且后续的future依赖前面future的执行结果, 所以如果存在并行执行的future, 需要事先在for
之外创建Future
.val one = cal(1, 5) val two = cal(2, 7) val three = cal(3, 9) for { o <- one d <- two t <- three flag <- check(o + d + t) } yield flag
在
for
语句之前,三个 Future 在创建之后就开始各自独立的运行。 而 check 是在前面3个future执行完毕后才开始执行.
执行上下文ExecutionContext
到现在为止,我们都是使用隐式可用的全局 ExecutionContext
来执行这些代码块。 通常,更好的方式是创建一个专用的 ExecutionContext
。 可以从 Java的 ExecutorService
来它,这也意味着,可以异步的调整线程池来执行数据库调用,应用的其他部分不受影响。
val executorService = Executors.newFixedThreadPool(4)
implicit val executionContext = ExecutionContext.fromExecutorService(executorService)
最好有一些专属的 ExecutionContext
来处理不同场景的计算。怎样调整这些线程池大小取决于应用的特征.
参考: 类型 Future
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。