1. 程式人生 > >快學Scala學習筆記及習題解答(12-14高階函式、集合、模式匹配和樣例類)

快學Scala學習筆記及習題解答(12-14高階函式、集合、模式匹配和樣例類)

本文Scala使用的版本是2.11.8

第12章 高階函式

12.1 基本用法

作為值得函式

import scala.math._

val num = 3.14
// ceil函式後的_表示這是個函式,而不是忘記傳參
val fun = ceil _

// 呼叫
fun(num)

// 傳遞
Array(3.14, 1.42, 2.0).map(fun)

匿名函式

// 存放到變數
val triple = (x: Double) => 3 * x

// 傳遞給另一個函式
Array(3.14, 1.42, 2.0).map((x: Double) => 3 * x)

定義接收函式引數的函式

def valueAtOneQuarter(f: (Double) => Double) = f(0.25)

// 型別為((Double) => Double) => Double

// 也可以產生另一個函式
def mulBy(factor: Double) = (x: Double) => factor * x
// 型別為(Double) => ((Double) => Double)

引數型別推斷

Scala會盡可能幫助推斷型別資訊。例如,如果引數在=>右側只出現一次,可以用_替換它。

valueAtOneQuarter
(3 * _)

一些有用的高階函式

函式名 描述
map 將一個函式應用到某個集合的所有元素並返回結果
foreach 將函式應用到每個元素,無返回值
filter 輸出所有匹配某個特定條件的元素
reduceLeft 接受一個二元的函式,並將它應用到序列中的所有元素,從左到右
sortWith 接受一個二元函式,進行排序

閉包

def mulBy(factor: Double) = (x: Double) => factor * x

val triple = mulBy(3)
val half = mulBy(0.5
) println(triple(14) + " " + half(14)) // 將列印42 7

每一個返回的函式都有自己的factor設定。這樣的函式被稱作閉包(closure)。閉包由程式碼和程式碼用到的任何非區域性變數定義構成。

這些函式實際上是以類的物件方式實現的。

12.2 SAM轉換

在Scala中,每當想要告訴另一個函式做某件事時,會傳一個函式引數給它。而Java是將動作放在一個實現某介面的類中,然後將該類的一個例項傳遞給另一個方法。

這些介面被稱做SAM型別(single abstract method)。

Java實現

import java.awt.event.{ActionEvent, ActionListener}
import javax.swing.{JButton, JFrame}


object Note1 {

    implicit def makeAction(action: (ActionEvent) => Unit) = new ActionListener {
        override def actionPerformed(event: ActionEvent) {
            action(event)
        }
    }

    def main(args: Array[String]) {

        var data = 0
        val frame = new JFrame("SAM Testing");
        val jButton = new JButton("Counter")

        jButton.addActionListener(new ActionListener {
            override def actionPerformed(event: ActionEvent) {
                data += 1
                println(data)
            }
        })
    }
}

Scala實現

import java.awt.event.{ActionEvent, ActionListener}
import javax.swing.{JButton, JFrame}


object Note1 {

    // 定義隱式轉換,把函式轉換成一個ActionListener例項
    implicit def makeAction(action: (ActionEvent) => Unit) = new ActionListener {
        override def actionPerformed(event: ActionEvent) {
            action(event)
        }
    }

    def main(args: Array[String]) {

        var data = 0
        val frame = new JFrame("SAM Testing");
        val jButton = new JButton("Counter")

        // 傳遞函式引數
        jButton.addActionListener((event: ActionEvent) => {
            data += 1; println(data)
        })

        frame.setContentPane(jButton);
        frame.pack();
        frame.setVisible(true);
    }
}

12.3 柯里化

柯里化(currying)指的是將原來接受兩個引數的函式變成新的接受一個引數的函式的過程。新的函式返回一個以原有第二個引數做為引數的函式。

def mulOneAtATime(x: Int) = (y: Int) => x * y

// 呼叫
mulOneAtATime(6)(7)

// Scala支援如下簡寫來定義這樣的柯里化函式
def mulOneAtATime(x: Int)(y: Int) = x * y

示例

val a = Array("Hello", "World")
val b = Array("hello", "world")
a.corresponds(b)(_.equalsIgnoreCase(_))

12.4 控制抽象

/**
  * 如下: runInThread2方式定義函式就比runInThread要優雅很多
  * 呼叫時, 不用輸入 () =>
  */
object Note2 {

    def runInThread(block: () => Unit): Unit = {
        new Thread {
            override def run() { block() }
        }.start()
    }

    def runInThread2(block: => Unit): Unit = {
        new Thread {
            override def run() { block }
        }.start()
    }

    def main(args: Array[String]) {
        runInThread { () => println("Hi"); Thread.sleep(10000); println("Bye") }

        runInThread2 { println("Hi"); Thread.sleep(10000); println("Bye") }
    }
}

通過上面這樣的應用,可以構建控制抽象:看上去像是程式語言的關鍵字的函式。

示例:實現像while語句的函式

object Note2 {

    // 函式引數的專業術語叫換名呼叫引數, 和常規的引數不同, 函式在被呼叫時,
    // 引數表示式不會被求值, 如下 x == 0
    def until(condition: => Boolean) (block: => Unit): Unit = {
        if (!condition) {
            block
            until(condition)(block)
        }
    }

    def main(args: Array[String]): Unit = {
        var x = 10
        until (x == 0) {
            x -= 1
            println(x)
        }
    }
}

12.5 return表示式

object Note2 {

    // 函式引數的專業術語叫換名呼叫引數, 和常規的引數不同, 函式在被呼叫時,
    // 引數表示式不會被求值, 如下 x == 0
    def until(condition: => Boolean)(block: => Unit): Unit = {
        if (!condition) {
            block
            until(condition)(block)
        }
    }

    // 如果在帶名函式中使用return的話, 則需要給出其返回型別
    def indexOf(str: String, ch: Char): Int = {
        var i = 0
        until(i == str.length) {
            if (str(i) == ch) return i
            i += 1
        }
        -1
    }

    def main(args: Array[String]): Unit = {
        println(indexOf("test", 'x'))
    }
}

12.6 習題解答


1. 編寫函式values(fun: (Int) => Int, low: Int, high: Int),該函式輸出一個集合,對應給定區間內給定函式的輸入和輸出。比如,values(x => x * x, -5, 5)應該產出一個對偶的集合(-5, 25), (-4, 16), (-3, 9), …, (5, 25)

object One {

    def values(fun: (Int) => Int, low: Int, high: Int) = {
        var array = List[(Int, Int)]()
        low to high foreach {
            x => array = array :+ (x, fun(x))
        }
        array
    }

    def main(args: Array[String]) {
        println(values(x => x * x, -5, 5).mkString(" "))
    }
}

// 結果
(-5,25) (-4,16) (-3,9) (-2,4) (-1,1) (0,0) (1,1) (2,4) (3,9) (4,16) (5,25)


2. 如何用reduceLeft得到陣列中的最大元素?

object Two {

    def main(args: Array[String]) {
        val arr = Array(1, 333, 4, 6, 4, 4, 9, 32, 6, 9, 0, 2)
        val max = arr.reduceLeft((x, y) => {
            if (x > y) x else y
        })
        println(max)
    }
}

// 結果
333


3. 用to和reduceLeft實現階乘函式,不得使用迴圈或遞迴

object Three {

    def factorial(n: Int): Int = {
        if (n > 0) {
            1 to n reduceLeft (_ * _)
        } else if (n == 0) {
            1
        } else {
            throw new IllegalArgumentException("請輸入非負數.")
        }
    }

    def main(args: Array[String]) {
        println(factorial(-1))
    }
}

// 結果
120


4. 前一個實現需要處理一個特殊情況,即n<1的情況。展示如何用foldLeft來避免這個需要。(在scaladoc中查詢foldLeft的說明。它和reduceLeft很像,只不過所有需要結合在一起的這些值的首值在呼叫的時候給出。)

object Four {

    def factorial(n: Int): Int = {
        if (n < 0) {
            throw new IllegalArgumentException("請輸入非負數.")
        } else {
            (1 to n).foldLeft(1)(_ * _)
        }
    }

    def main(args: Array[String]) {
        println(factorial(0))
    }
}


5. 編寫函式largest(fun: (Int) => Int, inputs: Seq[Int]),輸出在給定輸入序列中給定函式的最大值。舉例來說,largest(x => 10 * x - x * x, 1 to 10)應該返回25。不得使用迴圈或遞迴

object Five {

    def largest(fun: (Int) => Int, inputs: Seq[Int]): Int = {
        inputs.map(fun).max
    }

    def main(args: Array[String]) {
        println(largest(x => 10 * x - x * x, 1 to 10))
    }
}


6. 修改前一個函式,返回最大的輸出對應的輸入。舉例來說,largestAt(fun: (Int) => Int, inputs: Seq[Int])應該返回5。不得使用迴圈或遞迴

object Six {

    def largestAt(fun: (Int) => Int, inputs: Seq[Int]): Int = {
        inputs.map(x => (x, fun(x)))
            .reduceLeft((x, y) => if (x._2 > y._2) x else y)._1
    }

    def main(args: Array[String]) {
        println(largestAt(x => 10 * x - x * x, 1 to 10))
    }
}


7. 要得到一個序列的對偶很容易,比如:
val pairs = (1 to 10) zip (11 to 20)

假定你想要對這個序列做某種操作,比如,給對偶中的值求和,但是你不能直接使用:
pairs.map( + )
函式 _ + _ 接受兩個Int作為引數,而不是(Int, Int)對偶。編寫函式adjustToPair,該函式接受一個型別為(Int, Int) => Int的函式作為引數,並返回一個等效的, 可以以對偶作為引數的函式。舉例來說就是:adjustToPair(_ * _)((6, 7))應得到42。然後用這個函式通過map計算出各個對偶的元素之和

object Seven {

    def ajustToPair(fun: (Int, Int) => Int) = (x: (Int, Int)) => fun(x._1, x._2)

    def main(args: Array[String]) {
        val x = ajustToPair(_ * _)((6, 7))
        println(x)
        val pairs = (1 to 10) zip (11 to 20)
        println(pairs)
        val y = pairs.map(ajustToPair(_ + _))
        println(y)
    }
}

// 結果
42
Vector((1,11), (2,12), (3,13), (4,14), (5,15), (6,16), (7,17), (8,18), (9,19), (10,20))
Vector(12, 14, 16, 18, 20, 22, 24, 26, 28, 30)


8. 在12.8節中,你看到了用於兩組字串陣列的corresponds方法。做出一個對該方法的呼叫,讓它幫我們判斷某個字串數組裡的所有元素的長度是否和某個給定的整數陣列相對應

object Eight {

    def main(args: Array[String]) {
        val a = Array("asd", "df", "abcd")
        val b = Array(3, 2, 4)
        val c = Array(3, 2, 1)
        println(a.corresponds(b)(_.length == _))
        println(a.corresponds(c)(_.length == _))

    }
}

// 結果
true
false


9. 不使用柯里化實現corresponds。然後嘗試從前一個練習的程式碼來呼叫。你遇到了什麼問題?

沒有柯里化則不能使用前一個練習裡的程式碼方式來呼叫


10. 實現一個unless控制抽象,工作機制類似if,但條件是反過來的。第一個引數需要是換名呼叫的引數嗎?你需要柯里化嗎?

// 需要換名和柯里化
object Ten {

    def unless(condition: => Boolean)(block: => Unit) {
        if (!condition) {
            block
        }
    }

    def main(args: Array[String]) {
        unless(0 > 1) {
            println("Unless!")
        }
    }
}

第13章 集合

13.1 主要概念

所有的集合都擴充套件自Iterable。有三類集合,分別是Seq、Set和Map。

Iterable提供了遍歷一個集合的基本方式。

每個集合特質或類都有一個帶有apply方法的伴生物件,可以用於構建該集合中的例項。

Seq

Seq是一個有先後次序的序列,比如陣列和列表。IndexedSeq是它的子特質,允許通過下標訪問元素。

Set

Set是沒有次序的一組值,子特質SortedSet的元素則以某種排過序的順序被訪問。

Map

Map是一組對偶。SortedMap按照鍵的排序訪問其中的實體。

可變和不可變集合

不可變的集合從不改變,因此可以安全地共享其引用。

13.2 序列

不可變序列

不可變序列

Vector是ArrayBuffer的不可變版本:可下標訪問,支援快速隨機訪問。它是以樹形結構的形式實現的,每個節點可以有不超過32個子節點。

Range表示一個整數序列,它並不儲存所有值,而只是起始值、結束值和增值。

可變序列

可變序列

13.3 列表

不可變列表

列表要麼是Nil(即空表),要麼是一個head元素加上一個tail,而tail又是一個列表。

val digits = List(4, 2)
// digits.head值是4,digits.tail是List(2)。
// digits.tail.head2,而digits.tail.tail是Nil

::操作符從給定的頭和尾建立一個新的列表

9 :: List(4, 2)
// 返回 List(9, 4, 2)
// 也可以如下(::是右結合的)
9 :: 4 :: 2 :: Nil

列表遍歷,可以使用迭代器、遞迴或者模式匹配。下面是模式匹配示例:

def sum(lst: List[Int]): Int = lst match {
    case Nil => 0
    case h :: t => h + sum(t)
}

可變列表

可變的LinkedList和不可變List相似。不過,可以通過對elem引用賦值來修改其頭部,對next引用賦值修改其尾部。

還提供了DoubleLinkedList,區別是多帶一個prev引用。


如果要把某個節點變成列表中的最後一個節點,不能將next引用設為Nil,而是設定為LinkedList.empty

13.4 集

集市不重複元素的集合。集並不保留元素插入的順序,預設以雜湊集實現的。

鏈式雜湊集可以記住元素被插入的順序。

val weekdays = scala.collection.mutable.LinkedHashSet("Mo", "Tu", "We", "Th", "Fr")

已排序的集(用紅黑樹實現):

scala.collection.immutable.SortedSet(1, 2, 3, 4, 5)

位集(BitSet)是以一個字位序列的方式存放非負整數。如果集中有i,則第i個字位是1。提供了可變和不可變的實現。

contains方法檢查某個集是否包含給定的值。subsetOf方法檢查某個集是否是另一個集的子集。

val digits = Set(1, 7, 2, 9)
digits contains 0 // false
Set(1, 2) subsetOf digits // true

union、intersect和diff方法執行通常的集操作。也可以寫作|&&~。還可以將聯合(union)寫作++,將差異(diff)寫作--

val primes = Set(2, 3, 5, 7)
digits union primes 等於 Set(1, 2, 3, 5, 7, 9)
digits & primes 等於 Set(2, 7)
digits -- primes 等於 Set(1, 9)

13.5 新增和去除元素的操作符

操作符 描述 集合型別
coll :+ elem
elem +: coll
有elem被追加到尾部或頭部的與coll型別相同的集合 Seq
coll + elem
coll + (e1, e2, …)
添加了給定元素的與coll型別相同的集合 Set、Map
coll - elem
coll - (e1, e2, …)
給定元素被移除的與coll型別相同的集合 Set、Map、ArrayBuffer
coll ++ coll2
coll2 ++: coll
與coll型別相同的集合,包含了兩個集合的元素 Iterable
coll – coll2 移除了coll2中元素的與coll型別相同的集合(用diff來處理序列) Set、Map、ArrayBuffer
elem :: lst
lst2 ::: lst
被向前追加了元素或給定列表的列表。和+:以及++:的作用相同 List
list ::: list2 等同於list ++: list2 List
set | set2
set & set2
set &~ set2
並集、交集和兩個集的差異。|等於++,&~等於– Set
coll += elem
coll += (e1, e2, …)
coll ++= coll2
coll -= elem
coll -= (e1, e2, …)
coll –= coll2
通過新增或移除給定元素來修改coll 可變集合
elem +=: coll
coll2 ++=: coll
通過向前追加給定元素或集合來修改coll ArrayBuffer


一般,+用於將元素新增到無先後次序的集合,而+:和:+則是將元素新增到有先後次序的集合的開頭或末尾。

Vector(1, 2, 3) :+ 5 // Vector(1, 2, 3, 5)
1 +: Vector(1, 2, 3) // Vector(1, 1, 2, 3)


和其他以冒號結尾的操作符一樣,+:是右結合的。

提示彙總如下

  1. 向後(:+)或向前(+:)追加元素到序列當中。
  2. 新增(+)元素到無先後次序的集合中。
  3. 用-移除元素。
  4. 用++和–來批量新增和移除元素。
  5. 對於列表,優先使用::和:::。
  6. 改值操作用+=、++=、-=和–=。
  7. 對於集合,更喜歡++、&和–。
  8. 儘量不用++:、+=:和++=:。
  9. 對於列表可以用+:而不是::來保持與其他集合操作的一致性。但有一個例外:模式匹配(case h::t)不認+:操作符。

13.6 常用方法

Iterable特質

方法 描述
head、last、headOption、lastOption 返回第一個或最後一個元素,或者以Option返回
tail、init 返回除第一個或最後一個元素外其餘部分
length、isEmpty 返回集合長度;或者,當長度為零時返回true
map(f)、foreach(f)、flatMap(f)、collect(pf) 將函式用到所有元素
reduceLeft(op)、reduceRight(op)、foldLeft(init)(op)、foldRight(init)(op) 以給定順序將二元操作應用到所有元素
reduce(op)、fold(init)(op)、aggregate(init)(op, combineOp) 以非特定順序將二元操作應用到所有元素
sum、product、max、min 返回和或乘積(前提元素型別可被隱式轉換為Numeric特質);或者最大值、最小值(前提可隱式轉換成Ordered特質)
count(pred)、forall(pred)、exists(pred) 返回滿足前提表示式的元素計數;所有元素都滿足時返回true;或者至少有一個元素滿足時返回true。
filter(pred)、filterNot(pred)、partition(pred) 返回所有滿足前提表示式的元素;所有不滿足的元素;或者,這兩組元素組成的對偶
takeWhile(pred)、dropWhile(pred)、span(pred) 返回滿足前提表示式的一組元素(直到遇到第一個不滿足的元素);所有其他元素;或者,這兩組元素組成的對偶
take(n)、drop(n)、splitAt(n) 返回頭n個元素;所有其他元素;或者,這兩組元素組成的對偶
takeRight(n)、dropRight(n) 返回最後n個元素,或者所有其他元素
slice(from, to) 返回從from到to這個區間內的所有元素
zip(coll2)、zipAll(coll2, fill, fill2)、zipWithIndex 返回由本集合元素和另一個集合的元素組成的對偶
grouped(n)、sliding(n) 返回長度為n的子集迭代器;grouped產出下標為0 until n的元素,然後是下標為n until 2 * n的元素,以此類推;sliding產出下標為0 until n的元素,然後是下標為1 until n + 1的元素,以此類推
mkString(before, between, after)、addString(sb, before, between, after) 做出一個有所有元素組成的字串,將給定字串分別新增到首個元素之前、每個元素之間,以及最後一個元素之後。第二個方法將該字串追加到字串構建器當中
toIterable、toSeq、toIndexedSeq、toArray、toList、toStream、toSet、toMap 將集合轉換成指定型別集合
copyToArray(arr)、copyToArray(arr, start, length)、copyToBuffer(buf) 將元素拷貝到陣列或緩衝當中

Seq特質

方法 描述
contains(elem)、containsSlice(seq)、startsWith(seq)、endsWith(seq) 返回true,如果該序列:包含給定元素;包含給定序列;以給定序列開始;或者,以給定序列結束
indexOf(elem)、lastIndexOf(elem)、indexOfSlice(seq)、lastIndexOfSlice(seq) 返回給定元素或序列在當前序列中的首次或末次出現的下標
indexWhere(pred) 返回滿足pred的首個元素的下標
prefixLength(pred)、segmentLength(pred, n) 返回滿足pred的最長元素序列的長度,從當前序列的下標0或n開始查詢
padTo(n, fill) 返回當前序列的一個拷貝,將fill的內容向後追加,直到新序列長度達到n
intersect(seq)、diff(seq) 返回交集;或者差值
reverse 當前序列反向
sorted、sortWith(less)、sortBy(f) 使用元素本身的大小、二元函式less,或者將每個元素對映成一個帶先後次序的型別的值得函式f,對當前序列進行排序後的新序列
premutations、combinations(n) 返回一個遍歷所有排列或組合(長度為n的子序列)的迭代器

13.7 將函式對映到集合

val names = List("Peter", "Paul", "Mary")

names.map(_.toUpperCase) // 結果:List("PETER", "PAUL", "MARY")
// 等效於以下操作
for (n <- names) yield n.toUpperCase

// 將所有值連線到一塊
def ulcase(s: String) = Vector(s.toUpperCase(), s.toLowerCase())

names.map(ulcase)
// 得到 List(Vector("PETER", "peter"), Vector("PAUL", "paul"), Vector("MARY", "mary"))

names.flatMap(ulcase)
// 得到 List("PETER", "peter", "PAUL", "paul", "MARY", "mary")

collect方法用於偏函式(partial function)

"-3+4".collect { case '+' => 1; case '-' => -1 }
// 得到 Vector(-1, 1)

foreach

names.foreach(println)

13.8 化簡、摺疊和掃描

主要說明方法用二元函式來組合集合中的元素。

List(1, 7, 3, 9).reduceLeft(_ - _)
// 相當於 ((1 - 7) - 2) - 9 = -17

List(1, 7, 3, 9).reduceRight(_ - _)
// 相當於 1 - (7 - (2 - 9)) = -13

// 以不同於集合首元素的初始元素計算通常也很有用
List(1, 7, 2, 9).foldLeft(0)(_ - _)
// 相當於(((0 - 1) - 7) - 2) - 9
// 也可以像下面這樣代替foldLeft操作
(0 /: List(1, 7, 2, 9))(_ - _)

// 同樣也提供了foldRight或:\

// scanLeft和scanRight方法將摺疊和對映操作結合在一起
scala> (1 to 10).scanLeft(0)(_ + _)
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)

scala> (1 to 10).scanRight(0)(_ + _)
res1: scala.collection.immutable.IndexedSeq[Int] = Vector(55, 54, 52, 49, 45, 40, 34, 27, 19, 10, 0)

13.9 拉鍊操作

拉鍊操作指的是兩個集合,把它們相互對應的元素結合在一起。

val prices = List(5.0, 20.0, 9.95)
val quantities = List(10, 2, 1)

// zip方法將它們組合成一個對偶的列表
prices zip quantities
// 得到List[(Double, Int)] = List((5.0, 10), (20.0, 2), (9.95, 1))

// 獲得價格的列表
(prices zip quantities) map { p => p._1 * p._2 }
// 所有物件的總價
((prices zip quantities) map { p => p._1 * p._2 }).sum

// 如果元素長度不等,則返回最終元素的個數與較短的一致

// zipAll方法可以指定較短列表的預設值
List(5.0, 20.1, 9.4).zipAll(List(10, 2), 1.2, 1)
// 其中zipAll第二個引數用於填充前面的列表;而第三個引數填充後面的列表

// zipWithIndex返回對偶列表,其中每個對偶中第二個組成部分是每個元素的下標
"Scala".zipWithIndex
res4: scala.collection.immutable.IndexedSeq[(Char, Int)] = Vector((S,0), (c,1), (a,2), (l,3), (a,4))

13.10 迭代器

可以用iterator方法從集合獲得一個迭代器。

對於完整構造需要很大開銷的集合而言,迭代器很有用。

Source.fromFile產出一個迭代器。另外Iterable中的一些方法(grouped或sliding)產生迭代器。

13.11 流

流(stream)提供的是一個不可變的替代品,是一個尾部被懶計算的不可變列表。

def numsFrom(n: BigInt): Stream[BigInt] = n #:: numsFrom(n + 1)
// 通過#::構建流

val tenOrMore = numsFrom(10)
// 返回 Stream(10, ?)
tenOrMore.tail.tail.tail
// 返回 Stream(13, ?)

// 強制求所有值
tenOrMore.take(5).force

// 可以從迭代器構造一個流
val words = Source.fromFile("/usr/share/dict/words").getLines.toStream
words // Stream(A, ?)
words(5) // Aachen
words // Stream(A, A's, AOL, AOL's, Aachen, ?)

// 迭代器對於每行只能訪問一次,而流將快取訪問過的行,允許重新訪問

13.12 懶檢視

使用view方法,同樣可以獲得一個被懶執行的集合。

import scala.math._
val powers = (0 until 1000).view.map(pow(10, _))
// powers: scala.collection.SeqView[Double,Seq[_]] = SeqViewM(...)

powers(2)
// res12: Double = 100.0

// 也可以用force方法進行強制求值。

13.13 與Java集合的互操作

從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
asScalaConcurentMap concurrent.ConcurrentMap mutable.ConcurrentMap

13.14 執行緒安全的集合

Scala類庫提供了六個特質,可以將它們混入集合,讓集合的操作變成同步的:

SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
SynchronizedStack

示例

val scores = new scala.collection.mutable.HashMap[String, Int] with scala.collection.mutable.SynchronizedMap[String, Int]

13.15 並行集合

par方法產出集合的一個並行實現。

// 通過par並行化for迴圈,結果是按執行緒產出的順序輸出的
scala> for (i <- (0 until 100).par) print(i + " ")
50 25 0 75 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 26 27 28 29 30 31 32 33 34 35 36 37 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 38 39 40 41 42 43 44 45 46 47 48 49 87 88 89 90 91 92 93 94 95 96 97 98 99 81 82 83 84 85 86 78 79 80 76 77

// 在for/yield迴圈中,結果是依次組裝的
scala> for (i <- (0 until 100).par) yield i + " "
res15: scala.collection.parallel.immutable.ParSeq[String] = ParVector(0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , 48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , 62 , 63 , 64 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , 73 , 74 , 75 , 76 , 77 , 78 , 79 , 80 , 81 , 82 , 83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 91 , 92 , 93 , 94 , 95 , 96 , 97 , 98 , 99 )

par方法返回的並行集合的型別為擴充套件自ParSeq、ParSet或ParMap特質的型別,所有這些特質都是ParIterable的子型別。但這些並不是Iterable的子型別,因此不能講並行集合傳遞給預期Iterable、Seq、Set或Map的方法。

13.16 習題解答


1. 編寫一個函式,給定字串,產出一個包含所有字元的下標的對映。舉例來說:indexes(“Mississippi”)應返回一個對映,讓’M’對應集{0},’i’對應集{1,4,7,10},依此類推。使用字元到可變集的對映。另外,你如何保證集是經過排序的?