Chap 0 前言
focus on:
Scala 的语法十分简洁
Scala 运行在虚拟机之上, 可以使用 java 的海量类库和工具
Scala 拥抱函数式编程的同时,并没有废弃面向对象
Scala 既有动态语言那样的灵活简洁,同时有保留了静态类型检查的安全与执行效率
Scala 既能处理脚本化的临时任务,又能处理高并发场景下的分布式互联网大数据应用,可谓能缩能伸
Chap 1 基础
focus on:
使用 scala 解释器
用 var 和 val 定义变量
数字类型
使用操作符和函数
浏览 Scaladoc
1.1 Scala解释器
scala> 8 * 5 + 2
res1: Int = 42
scala> 0.5 * res1
res4: Double = 21.0
scala> "Hello, " + res1
res5: String = Hello, 42
{ String = java.lang.String }
scala 程序并不是一个解释器。
输入的内容被快速地编译成字节码,然后这段字节码交由 Java 虚拟机执行。
我们称之为 : REPL
scala> res5.to // tab 补全
toByte toChar toDouble toFloat toInt toLong toShort toString
scala> res5.toUpperCase
res6: String = HELLO, 42
1.2 声明值和变量
Scala 鼓励使用 val, 除非你真的需要改变它。声明值和变量不初始化会报错。
注: 你不需要给出值和变量的类型,scala会根据初始化的值推断出来。
scala> val answer = 8 * 5 + 2
answer: Int = 42
scala> answer * 0.5
res8: Double = 21.0
在必要的时候,你也可是指定类型
scala> val greeting: String = null
greeting: String = null
scala> val greeting: Any = "Hello"
greeting: Any = Hello
scala> val xmax, ymax = 100
xmax: Int = 100
ymax: Int = 100
1.3 常用类型
Byte、Char、Short、Int、Long、Float、Double。和 Boolean。
与 Java 不同的是,这些类型是 类。
scala> 1.toString()
res9: String = 1
scala> 1.to(10)
res10: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
Scala不需要包装类型。在基本类型和包装类型之间转换是 Scala 编译器的工作。
Scala 用 java.lang.String 类来表示字符串。不过,它通过 StringOps 类给字符串追加了上百种操作。
举例 intersect :
scala> "Hello".intersect("World")
res11: String = lo
在这个表达式中,java.lang.String 对象 "Hello" 被隐式地转换成一个 StringOps 对象,接着 StringOps 类的 intersect 方法被应用。
同样 Scala 还提供了 RichInt、RichDouble、RichChar 等 to 方法就是 RichInt 类中的方法。
还有 BigInt 和 BigDecimal类,用于任意大小(但有穷)的数字。这些类背后是 java.math.BigInteger 和 java.math.Bigdecimal,在scala中,他们用起来更方便,可以用常规操作符来操作它们。
scala> val x: BigInt = 1234567890
x: BigInt = 1234567890
scala> x * x * x
res13: scala.math.BigInt = 1881676371789154860897069000
1.4 算术/操作符重载
+ - * / % 位 & | ^ >> << 都是完成通常的工作。
只是有一点特别,这些操作符实际上是方法。
a + b 其实是 a.+(b) + 是方法名。
Scala 并不会傻乎乎的 对方法名中使用非字母或数字 这种做法 带有偏见
如 BigInt 类就定义了一个名为 /% 的方法,该方法返回一个对偶 (商、余数)
1.to(10) 也可以写成 1 to 10.
Scala 没有提供 ++, -- 这种操作符。
1.5 调用函数和方法
除了方法之外,scala 还提供函数。相比 Java,在 Scala 中使用数学函数 (比如 : min 或 pow) 更为简单 ---- 你不需要从某个类调用它的静态方法
scala> import scala.math._ 或 import math._ 说明: _ 通配符类似java *
import scala.math._
scala> sqrt(2) 或者 math.sqrt(2)
res15: Double = 1.4142135623730951
scala> pow(2, 4)
res16: Double = 16.0
scala> min(3, Pi)
res17: Double = 3.0
Scala 没有静态方法,
Scala有一个特性,叫做单例对象(singleton object)
通常一个类对应有一个 伴生对象 (companion object),其方法就跟 Java 中的静态方法一样。举例来说,BigInt 类的 BigInt 伴生对象有一个生成指定位数的随机素数的方法 probablePrime:
scala> BigInt.probablePrime(100, scala.util.Random)
res33: scala.math.BigInt = 882882747840768533709728498879
说明 : 这里的 Random 是一个单例随机数生成器对象,而该对象是在 scala.util 包中定义的。这里用单例对象比用类更好的为数不多的场景之一。在Java中,为每个随机数都构造出一个新的java.util.Random对象是一个常见的错误。
Scala 没有参数且不改变当前对象的方法不带圆括号。如 :
scala> "Hello".distinct
res34: String = Helo
1.6 apply 方法
在 Scala 中,我们通常都会使用类似函数调用的语法。
scala> "Hello"(4)
res35: Char = o
相当于 C++ s[i], Java 的 s.charAt(i)
举例来说 在 StringOps 类的文档中,你会发现这样一个方法
def apple(n: Int): Char
"Hello"(4) 相当于 "Hello".apply(4)
如果去看 BigInt 伴生对象的文档,就会看到让你将字符串或数字转换为 BigInt 对象的 apply 方法。
scala> BigInt("12345")
res36: scala.math.BigInt = 12345
scala> BigInt.apply("12345")
res37: scala.math.BigInt = 12345
这个语句产生一个新的 BigInt 对象,不需要使用 new。
使用伴生对象apply方法是 Scala中 构建对象的常用手法
scala> Array(1, 4, 9, 16)
res38: Array[Int] = Array(1, 4, 9, 16)
scala> Array.apply(1, 4, 9, 16)
res39: Array[Int] = Array(1, 4, 9, 16)
1.7 Scaladoc
Java 程序员使用 Javadoc 浏览 Java API。
Scaladoc www.scala-lang.org/api 在线浏览 Scaladoc
www.scala-lang.org/download#api
注意每个类名旁边的 O 和 C,它们分别链接到对应的类 (C) 或 伴生对象 (O).
如果你想使用数值类型,记得看看 RichInt、RichDouble等。字符串看StringOps
数学函数 scala.math 包中
BigInt 有一个方法叫做 unary_-. 这就是你定义前置的负操作符 -x 的方式
标记为 implicit 的方法对应的是自动(隐式)转换。比如: BigInt 对象拥有在需要时自动被调用的由 int 和 long 转换为 BigInt 的方法。
方法可以以函数作为参数。 如 def count(p: (Char) => Boolean) : Int
调用类似方法时,你通常可以一种非常紧凑的表示法给出函数定义。
scala> var s = "HelLo"
s: String = HelLo
scala> s.count(_.isUpper)
res42: Int = 2
-
最后,当你偶尔遇到类似 StringOps 类中这样的看上去几乎没法一下子理解的方法签名时. 例如下情况 :
def patch [B >: Char, That](from: Int, patch: GenSeq[B], replaced: Int) (implicit bf: CanBuildFrom[String, B, That]): That
-
别紧张,直接忽略即可,还有另一个版本的 patch, 看上去容易讲得通
def patch(from: Int, that: GenSeq[Char], replace: Int): StringOps[A]
-
如果你把 GenSeq[Char] 和 StringOps[A] 都当做 String 的话,这个方法从文档理解起来就简单多了。当然,在 REPL 中试用也很容易:
scala> "Harry".patch(1, "ung", 2) res43: String = Hungry
Chap 2 控制结构和函数
focus on:
Scala 中,几乎所有的构造出来语法结构都有值。(区别于Java语句没有值)
if、块、 表达式 有值
void 类型是 Unit
避免在函数定义中使用 return
注意别在函数式定义中漏掉了=。
Scala 没有受检异常
2.1 条件表达式
scala> var x = -4
x: Int = -4
scala> val s = if (x > 0) 1 else -1
s: Int = -1
scala> if (x > 0) "positive" else -1
res0: Any = -1
{说明 : 类型不同,返回值为公共超类型 Any}
scala> if (x > 0) 1
res1: AnyVal = ()
scala> if (x > 0) 1 else ()
res2: AnyVal = ()
{说明 : 这两条语句相同。()=Unit, (java void)}
2.2 块表达式-赋值
scala> val k = {val dx = 2; val dy = 3; math.sqrt(dx * dx + dy * dy)}
k: Double = 3.605551275463989
scala> x = y = 1
<console>:9: error: type mismatch;
found : Unit
required: Int
x = y = 1
^
{注意 : 赋值语句的值是 Unit}
2.3 输入和输出
scala> print("Answer: ")
Answer:
scala> print(42)
42
scala> println("Answer : " + 42)
Answer : 42
scala> // C风格 的 printf
scala> printf("Hello, %s! You are %d years old. \n", "fern", 28)
Hello, fern! You are 28 years old.
从控制台读取 readLine, readInt, readDouble ...
scala> val name = readLine("Your name : ")
warning: there was one deprecation warning; re-run with -deprecation for details
Your name : name: String = Bean
scala> val age = readInt()
warning: there was one deprecation warning; re-run with -deprecation for details
age: Int = 25
readDouble、readByte、readShort、readLong、
readFloat、readBoolean、readChar。
2.4 循环
scala> var n = 3
n: Int = 3
scala> var r = 1
r: Int = 1
scala> :paste
// Entering paste mode (ctrl-D to finish)
while (n > 0) {
r = r * n
n -= 1
}
// Exiting paste mode, now interpreting.
scala> print(n)
0
scala> print(r)
6
scala> var n = 3
n: Int = 3
scala> 1 to n
res13: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)
scala> for (i <- 1 to n) {
| r = r*i
| }
scala> print(r)
36
说明 :
RichInt 类 这个 to 方法,返回 Range(区间)
在 for 循环的变量之前并没有 val 或 var 的指定。该变量的类型是集合的原色类型。循环变量的作用域一直持续到循环结束。
scala> val s = "Hello"
s: String = Hello
scala> var sum = 0
sum: Int = 0
scala> for (i <- 0 until s.length)
| sum += s(i)
scala> sum
res7: Int = 500
scala> var sum = 0
sum: Int = 0
你可以直接遍历字符序列,不需要使用下标
scala> for (ch <- "Hello") sum += ch
scala> sum
res9: Int = 500
说明 : Scala 并没有直接提供 break 或 continue 语句来退出循环。
2.5 for循环、for推导式
可以以 变量 <- 表达式 的形式提供多个生成器,用分号将它们隔开。
scala> for (i <- 1 to 3; j <- 1 to 3) print ((10*i + j) + " ")
11 12 13 21 22 23 31 32 33
生成器可以带着守卫条件
scala> for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10*i + j) + " ")
12 13 21 23 31 32
注意 : if 之前没有 分号
===
你可以使用任意多的定义,引入可以在循环中使用的变量 :
scala> for (i <- 1 to 3; from = 4 - i; j <- from to 3) print ((10*i + j) + " ")
13 22 23 31 32 33
循环体以 yield 开始,则该循环会构造出一个集合,每次迭代生成集合中的一个值
scala> for (i <- 1 to 10) yield i % 3
res12: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 0, 1, 2, 0, 1, 2, 0, 1)
这叫做 for推导式。 for推导式 生成的集合 与 它的第一个生成器是类型兼容的
scala> for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar
res16: String = HIeflmlmop
scala> for (i <- 0 to 1; c <- "Hello") yield (c + i).toChar
res17: scala.collection.immutable.IndexedSeq[Char] = Vector(H, e, l, l, o, I, f, m, m, p)
scala> 0 to 3
res20: scala.collection.immutable.Range.Inclusive = Range(0, 1, 2, 3)
2.7 函数
定义函数,给出: 名称、参数、函数体
scala> def abs(x: Double) = if (x >= 0) x else -x
abs: (x: Double)Double
scala> abs(-5)
res22: Double = 5.0
scala> def fac(n: Int) = {
| var r = 1
| for (i <- 1 to n) r = r * i
| r
| }
fac: (n: Int)Int
scala> fac(3)
res23: Int = 6
我们最好适应没有 return 的日子,很快你就可以使用大量匿名函数。return 相当于 函数版的 break。
对于递归函数,必须指定返回类型,Scala 不是 Haskell, Scala 猜测不出来
scala> def fact(n: Int): Int = if (n <= 0) 1 else n * fact(n-1)
fact: (n: Int)Int
scala> fact(4)
res24: Int = 24
2.8 默认参数和带名参数
scala> def decorate(str: String, left: String = "[", right: String = "]") = left + str + right
decorate: (str: String, left: String, right: String)String
scala> decorate("Hello")
res26: String = [Hello]
scala> decorate(left = "<<<", str = "Hello", right = ">>>")
res29: String = <<<Hello>>>
2.9 变长参数
scala> def sum(args : Int*) = {
| var result = 0
| for (arg <- args) result += arg
| result
| }
sum: (args: Int*)Int
scala> val s = sum(1, 4, 9, 16, 25)
s: Int = 55
用 值的序列 调用是错误的。 告诉编译器 当做 参数序列 处理
scala> val s = sum(1 to 5)
<console>:8: error: type mismatch;
found : scala.collection.immutable.Range.Inclusive
required: Int
val s = sum(1 to 5)
^
scala> val s = sum(1 to 5: _*)
s: Int = 15
递归这样解决
scala> def resurSum(args: Int*): Int = {
| if (args.length == 0) 0
| else args.head + resurSum(args.tail: _*)
| }
resurSum: (args: Int*)Int
scala> resurSum(1, 2, 3, 4, 5)
res31: Int = 15
因为 序列的 head 是它的首个元素,tail 是 其他元素的序列。
2.10 过程
不返回值函数的特殊表示法。 不建议使用
2.11 懒值
当 val 被声明为 lazy 时,它的初始化将被推迟,直到我们首次对它取值。
lazy val words = scala.io.Source.fromFile("~/words").mkString
懒值对于开销较大的初始化语句而言十分有用。
可以把 懒值 当做是介于 val 和 def 的中间状态。
说明 : 懒值并不是没有额外开销。我们每次访问懒值,都会有一个方法被调用,而这个方法将会以线程安全的方式检查该值是否已被初始化。
2.12 异常
Scala 的异常工作机制 和 Java / C++ 类似。
如 :
throw new IllegalArgumentException("x should not be negative")
当前运算被中止,运行时系统查找可接受 IllegalArgumentException 的异常处理器
和 Java 一样,抛出的对象必须是 java.lang.Throwable 的子类。不过,与 Java 不同的是,Scala 没有 “受检” 异常 -- 你不需要声明说函数或方法可能会抛出某种异常。
throw 表达式有特殊的类型 Nothing。 这在 if/else 表达式中有用。如果一个分支的类型是 Nothing,那么 if/else 表达式的类型就是另一个分支的类型。举例来说, 考虑如下代码
scala> var x = -1
x: Int = -1
scala> if (x >= 0) {
| math.sqrt(x)
| } else throw new IllegalArgumentException("x should not be negative")
java.lang.IllegalArgumentException: x should not be negative
... 37 elided
第一个分支类型是 Double,第二个分支类型是 Nothing。 因此,if/else 表达式的类型是 Double。
捕获异常的语法采用的是模式匹配的语法。
scala> try {
| process(new URL("http://horstmann.com/fred-tiny.gif"))
| } catch {
| case _: MalformedURLException => println("Bad URL: " + url)
| case ex: IOException => ex.printStackTrace()
| }
和 Java 一样,更通用的异常应该排在更具体的异常之后。
注意,如果你需要使用捕获的异常对象,可以使用 _ 来替代变量名
try / finally 语句让你可以释放资源。
try { ... } finally { ... }
try { ... } catch { ... } finally { ... }
Chap 3 数组操作
focus on:
若 长度固定 则使用 Array, 若长度可能有变化则使用 ArrayBuffer。
提供初始值时不要使用 new
用 () 来访问元素
用 for (elem <- arr) 来遍历元素
用 for (elem <- arr if ...)...yield... 原数组转型为新数组
Scala数组 和 Java数组 可以互操作; 用 ArrayBuffer, 使用 scala.collection.JavaConversions 中的转换函数
3.1 定长数组
scala> val nums = new Array[Int](10)
nums: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
scala> val a = new Array[String](10)
a: Array[String] = Array(null, null, null, null, null, null, null, null, null, null)
已经提供初始值就不需要 new.
scala> val s = Array("Hello", "World")
s: Array[String] = Array(Hello, World)
scala> s(0) = "Goodbye"
scala> s
res7: Array[String] = Array(Goodbye, World)
scala> Array(2,3,5,7)
res8: Array[Int] = Array(2, 3, 5, 7)
Array(2,3,5,7) 在 JVM 中是 int[]
3.2 变长数组
Java ArrayList -- C++ vector -- Scala ArrayBuffer
scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer
scala> val b = ArrayBuffer[Int]()
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
scala> b += 1
res9: b.type = ArrayBuffer(1)
scala> b += (1, 2, 3, 5)
res11: b.type = ArrayBuffer(1, 1, 2, 3, 5)
scala> b ++= Array(8, 13, 21)
res12: b.type = ArrayBuffer(1, 1, 2, 3, 5, 8, 13, 21)
scala> b.trimEnd(5)
scala> b
res15: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 2)
在数组的尾端添加 或 删除元素是一个高效的操作。
在任意位置插入或移除元素是低效。(之后的元素都需要平移)
scala> b.insert(2, 6)
scala> b
res15: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 6, 2)
scala> b.insert(2, 7, 8, 9)
scala> b.remove(2)
res17: Int = 7
scala> b.remove(2, 3)
scala> b
res19: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 1, 2)
scala> b.toArray
res20: Array[Int] = Array(1, 1, 2)
scala> b.toBuffer
res22: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 1, 2)
scala> val c = b.toBuffer
c: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 1, 2)
scala> c
res24: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 1, 2)
scala> val c = b.toArray
c: Array[Int] = Array(1, 1, 2)
scala> c
res25: Array[Int] = Array(1, 1, 2)
3.3 遍历数组和数组缓冲
for (i <- 0 until a.length)
println(i + ": " + a(i))
0 until 10 高带 0.until(10)
0 until (a.length, 2)
// Range(0, 2, 4, ...)
0 until (a.length).reverse
for (elem <- a)
println(elem)
3.4 数组转换
scala> val a = Array(2, 3, 5, 7)
a: Array[Int] = Array(2, 3, 5, 7)
scala> val result = for (elem <- a) yield 2 * elem
result: Array[Int] = Array(4, 6, 10, 14)
// 从 数组缓冲 出发,也会得到另一个数组缓冲
scala> for (elem <- a if elem % 2 == 0) yield 2 * elem
res26: Array[Int] = Array(4)
scala> a
res27: Array[Int] = Array(2, 3, 5, 7)
函数式编程
scala> a.filter(_ % 2 == 0).map(2 * _)
res28: Array[Int] = Array(4)
考虑如下示例 :
给定一个整数的数组缓冲,我们想要移除除第一个负数之外的所有负数。
1). 收集保留的下标
var first = true
val indexs = for (i <- 0 until a.length if first || a(i) >= 0) yield { if (a(i) < 0) first = false; i }
2). 元素移动到该去的位置
for (j <- 0 until indexs.length) a(j) = a(indexs(j))
a.trimEnd(a.length - indexs.length)
3.5 常用算法
cala> Array(1, 7, 2).sum
res0: Int = 10
// ArrayBuffer the same
scala> Array(1, 7, 2).min
res1: Int = 1
scala> Array(1, 7, 2).max
res2: Int = 7
scala> Array(1, 7, 2.5).max
res4: Double = 7.0
scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer
scala> ArrayBuffer("Mary", "had", "little", "lamb").max
res9: String = little
sorted 方法将 Array or ArrayBuffer 排序并返回经过排序的 Array or ArrayBuffer
不改变原数组,产生新数组
scala> val b = ArrayBuffer(1, 7, 2, 9).sorted
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 7, 9)
scala> val b = ArrayBuffer(1, 7, 2, 9).sortWith(_>_)
b: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(9, 7, 2, 1)
改变原数组
scala> val a = Array(1, 7, 2, 9)
a: Array[Int] = Array(1, 7, 2, 9)
scala> scala.util.Sorting.quickSort(a)
scala> a
res11: Array[Int] = Array(1, 2, 7, 9)
对于 min, max 和 quickSort 方法,元素类型必须支持比较操作, 这包括了数字、字符串以及其他带有 Ordered 特质的类型。
显示 Array 或 ArrayBuffer 的内容,用 mkString
scala> a.mkString
res12: String = 1279
scala> a.mkString(",")
res13: String = 1,2,7,9
scala> a.mkString("<", ",", ">")
res14: String = <1,2,7,9>
scala> a.toString
res15: String = [I@6691eb1e
3.6 解读 Scaladoc
对 Array类 的操作方法列在 ArrayOps 相关条目下。从技术上讲,在数组上对用这些操作之前,数组都会被转换成 ArrayOps对象
3.7 多维数组
Double 的二维数组类型为 Array[Array[Double]]. 构造用 ofDim 方法。
scala> val matrix = Array.ofDim[Double](3, 4)
matrix: Array[Array[Double]] = Array(Array(0.0, 0.0, 0.0, 0.0), Array(0.0, 0.0, 0.0, 0.0), Array(0.0, 0.0, 0.0, 0.0))
scala> matrix(1)(2) = 42
scala> matrix
res19: Array[Array[Double]] = Array(Array(0.0, 0.0, 0.0, 0.0), Array(0.0, 0.0, 42.0, 0.0), Array(0.0, 0.0, 0.0, 0.0))
创建不规则数组
scala> val triangle = new Array[Array[Int]](10)
triangle: Array[Array[Int]] = Array(null, null, null, null, null, null, null, null, null, null)
scala> for (i <- 0 until triangle.length)
| triangle(i) = new Array[Int](i+1)
scala> triangle
res21: Array[Array[Int]] = Array(Array(0), Array(0, 0), Array(0, 0, 0), Array(0, 0, 0, 0), Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
3.8 与 Java 的互操作
可以引入 scala.collection.JavaConversions 里的隐式转换方法。
java.lang.ProcessBuilder类 有一个以 List<String> 为参数的构造器
import scala.collection.JavaConversions.bufferAsJavaList
import scala.collection.mutable.ArrayBuffer
scala> val command = ArrayBuffer("ls", "-al", "/home/data0")
command: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(ls, -al, /home/data0)
// Scala 到 Java 的转换
scala> val pb = new ProcessBuilder(command)
pb: ProcessBuilder = java.lang.ProcessBuilder@467eb8eb
import scala.collection.mutable.Buffer
import scala.collection.JavaConversions.asScalaBuffer
scala> val cmd: Buffer[String] = pb.command() // Java到Scala转换
cmd: scala.collection.mutable.Buffer[String] = ArrayBuffer(ls, -al, /home/data0)
scala> command
res25: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(ls, -al, /home/data0)
Chap 4 映射和元组
focus on:
Scala 有十分易用的语法来创建、查询、遍历映射
你需要从可变和不可变的映射中做出选择
默认你得到的是 哈希映射, 你也可以指明要 树形映射
Scala映射 和 Java映射 之间来回切换
元组可以用来聚集值
4.1 构造映射
不可变的映射
scala> val scores = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
scores: scala.collection.immutable.Map[String,Int] = Map(Alice -> 10, Bob -> 3, Cindy -> 8)
可变的映射
scala> val scores = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
scores: scala.collection.mutable.Map[String,Int] = Map(Bob -> 3, Alice -> 10, Cindy -> 8)
new 空映射
scala> val scores = new scala.collection.mutable.HashMap[String, Int]
scores: scala.collection.mutable.HashMap[String,Int] = Map()
Scala 中,映射是对偶的集合。
scala> "Alice"->10
res26: (String, Int) = (Alice,10)
scala> val scores = Map(("Alice", 10), ("Bob", 3))
scores: scala.collection.immutable.Map[String,Int] = Map(Alice -> 10, Bob -> 3)
4.2 获取映射中的值
scala> val bobsScore = scores("Bob")
bobsScore: Int = 3
scala> val bobsScore = if (scores.contains("Bob")) scores("Bob") else 0
bobsScore: Int = 3
scala> val bobsScore = if (scores.contains("Bob")) scores("Bobo") else 0
java.util.NoSuchElementException: key not found: Bobo
at scala.collection.MapLike$class.default(MapLike.scala:228)
at scala.collection.AbstractMap.default(Map.scala:59)
at scala.collection.MapLike$class.apply(MapLike.scala:141)
at scala.collection.AbstractMap.apply(Map.scala:59)
... 33 elided
scala> val bobsScore = if (scores.contains("Bobo")) scores("Bob") else 0
bobsScore: Int = 0
scala> val bobsScore = scores.getOrElse("Bob", 0)
bobsScore: Int = 3 // 快捷写法
4.3 更新映射中的值
scala> val scores = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
scores: scala.collection.mutable.Map[String,Int] = Map(Bob -> 3, Alice -> 10, Cindy -> 8)
scala> scores("Bob")=10
scala> val bobsScore = scores.getOrElse("Bob", 0)
bobsScore: Int = 10
scala> scores("Kevin")=100
scala> scores
res30: scala.collection.mutable.Map[String,Int] = Map(Bob -> 10, Kevin -> 100, Alice -> 10, Cindy -> 8)
scala> scores += ("Bob"->90, "Fred"->7)
res31: scores.type = Map(Bob -> 90, Kevin -> 100, Fred -> 7, Alice -> 10, Cindy -> 8)
scala> scores
res32: scala.collection.mutable.Map[String,Int] = Map(Bob -> 90, Kevin -> 100, Fred -> 7, Alice -> 10, Cindy -> 8)
scala> scores -= "Alice"
res33: scores.type = Map(Bob -> 90, Kevin -> 100, Fred -> 7, Cindy -> 8)
scala> scores
res34: scala.collection.mutable.Map[String,Int] = Map(Bob -> 90, Kevin -> 100, Fred -> 7, Cindy -> 8)
scala> scores -= "Alice00"
res36: scores.type = Map(Bob -> 90, Kevin -> 100, Fred -> 7, Cindy -> 8)
4.4 迭代映射
scala> scores.keySet
res40: scala.collection.Set[String] = Set(Bob, Kevin, Fred, Cindy)
scala> scores.values
res46: Iterable[Int] = HashMap(90, 100, 7, 8)
scala> for ((k, v) <- scores) yield { print (k, v) }
(Bob,90)(Kevin,100)(Fred,7)(Cindy,8)res42: scala.collection.mutable.Iterable[Unit] = ArrayBuffer((), (), (), ())
scala> for ((k, v) <- scores) yield (v, k)
res45: scala.collection.mutable.Map[Int,String] = Map(8 -> Cindy, 100 -> Kevin, 7 -> Fred, 90 -> Bob)
4.5 已排序映射
操作映射的时候,你需要选定一个实现 : 哈希表 or 平衡树. default hashtable
得到一个不可变的树形映射,而不是哈希映射
scala> val scores = scala.collection.immutable.SortedMap("Alice" -> 10, "Fred" -> 7, "Bob" -> 3, "Cindy" -> 8)
scores: scala.collection.immutable.SortedMap[String,Int] = Map(Alice -> 10, Bob -> 3, Cindy -> 8, Fred -> 7)
可变的树形映射,选择 Java 的 TreeMap。
按照插入顺序访问所有的键,使用 scala 的 LinkedHashMap
4.6 与 Java 的互操作
Java映射 转换为 Scala映射
scala> import scala.collection.JavaConversions.mapAsScalaMap
import scala.collection.JavaConversions.mapAsScalaMap
scala> val scores: scala.collection.mutable.Map[String, Int] = new java.util.TreeMap[String, Int]
scores: scala.collection.mutable.Map[String,Int] = Map()
java.util.Properties 到 Map[String, String] 的转换
scala> import scala.collection.JavaConversions.propertiesAsScalaMap
import scala.collection.JavaConversions.propertiesAsScalaMap
scala> val props: scala.collection.Map[String, String] = System.getProperties()
props: scala.collection.Map[String,String] =
Map(env.emacs -> "", java.runtime.name -> Java(TM) SE Runtime Environment, sun.boot.library.path -> /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib, java.vm.version -> 24.79-b02, user.country.format -> CN, gopherProxySet -> false, java.vm.vendor -> Oracle Corporation, java.vendor.url -> http://java.oracle.com/, path.separator -> :, java.vm.name -> Java HotSpot(TM) 64.Bit Server VM, file.encoding.pkg -> sun.io, user.country -> US, sun.java.launcher -> SUN_STANDARD, sun.os.patch.level -> unknown, java.vm.specification.name -> Java Virtual Machine Specification, user.dir -> /Users/hp, java.runtime.version -> 1.7.0_79-b15, java.awt.graphicsenv -> sun.awt.CGraphicsEnvironment, java.endorsed.dirs -> /Library/Java/JavaVirtual...
Scala映射 传递给 预期 Java映射 的方法,提供相反的隐式转换
import scala.collection.JavaConversions.mapAsJavaMap
import java.awt.font.TextAttribute._
scala> val attrs = Map(FAMILY -> "Serif", SIZE -> 12)
attrs: scala.collection.immutable.Map[java.awt.font.TextAttribute,Any] = Map(java.awt.font.TextAttribute(family) -> Serif, java.awt.font.TextAttribute(size) -> 12)
以下方法预期一个 java映射
scala> val font = new java.awt.Font(attrs)
font: java.awt.Font = java.awt.Font[family=Serif,name=Serif,style=plain,size=12]
scala> font
res47: java.awt.Font = java.awt.Font[family=Serif,name=Serif,style=plain,size=12]
scala> attrs
res48: scala.collection.immutable.Map[java.awt.font.TextAttribute,Any] = Map(java.awt.font.TextAttribute(family) -> Serif, java.awt.font.TextAttribute(size) -> 12)
不是很明白 java互操作?
4.7 元组
映射是 key/value 对偶 的集合。 对偶 是 元组 tuple 最简单的形态。
scala> val t = (1, 3.14, "Hello")
t: (Int, Double, String) = (1,3.14,Hello)
scala> val second = t._2
second: Double = 3.14
scala> val (first, second, _) = t
first: Int = 1
second: Double = 3.14
元组的下标从 1 开始。
元组可以用于函数返回不止一个值的情况。举例来说, StringOps 的 partition 方法返回的是一对字符串,分别包含了满足某个条件和不满足该条件的字符
scala> val y1 = "New York".partition(_.isUpper)
y1: (String, String) = (NY,ew ork)
4.8 拉链操作
使用元组的原因之一是把多个值绑在一起,便于它们被一起处理。
scala> val symbols = Array("<", "-", ">")
symbols: Array[String] = Array(<, -, >)
scala> val counts = Array(2, 10, 2)
counts: Array[Int] = Array(2, 10, 2)
scala> val pairs = symbols.zip(counts)
pairs: Array[(String, Int)] = Array((<,2), (-,10), (>,2))
scala> for ((s, n) <- pairs) Console.print(s * n)
<<---------->>
用 toMap 方法可以将对偶的集合转换成映射
scala> val pairs = symbols.zip(counts).toMap
pairs: scala.collection.immutable.Map[String,Int] = Map(< -> 2, - -> 10, > -> 2)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。