Scala Learn 1 Basic
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)