Scala 容許你指明函數的最後一個參數能夠是重複的。這能夠容許客戶向函數傳入可變長度參數列表。想要標註一個重複參數,在參數的類型以後加一個 *
。例如:html
def echo(args: String*) = { for (arg <- args) println(arg) }
這樣定義, echo 能夠被零個至多個 String 參數調用java
echo() echo("one") echo("hello", "world")
函數內部,重複參數的類型是聲明參數類型的數組。所以, echo 函數裏被聲明爲類型「 String* 」
的 args 的類型其實是 Array[String] 。然而,若是你有一個合適類型的數組,並嘗試把它看成
重複參數傳入,你會獲得一個編譯器錯誤:git
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 。數組
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會被覆蓋掉app
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, 此時就沒必要擔憂數據被覆蓋的問題了異步
在 scala.collection.convert
包下定義了經常使用的一些隱式轉換類ide
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._
便可函數
reduce: 對集合元素進行歸約操做, 默認從左到右兩兩運算, 而後結果與右邊的元素繼續運算, 最終獲得結果ui
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(_ + _)
使用 /:
(map1 /: map2).map { case (map, (k, v)) => map + (k -> v + map.getOrElse(k, 0))
查看源碼
實際上是以其中一個map做爲基礎, 將另外一個map的鍵值對作摺疊累加操做
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 { // do something ... ret }.onComplete { case Success(ret) => println(s"計算成功返回 $ret") case Failure(ex) => println("計算失敗, 異常: $ex") }
也能夠單獨使用 onSuccess
或 onFailure
分別註冊成功/失敗回調
全部容器類型均可以進行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
。 能夠從 Java的 ExecutorService
來它,這也意味着,能夠異步的調整線程池來執行數據庫調用,應用的其餘部分不受影響。
val executorService = Executors.newFixedThreadPool(4) implicit val executionContext = ExecutionContext.fromExecutorService(executorService)
最好有一些專屬的 ExecutionContext
來處理不一樣場景的計算。怎樣調整這些線程池大小取決於應用的特徵.
參考: 類型 Future