Chap 0 前言

focus on:

  1. Scala 的语法十分简洁

  2. Scala 运行在虚拟机之上, 可以使用 java 的海量类库和工具

  3. Scala 拥抱函数式编程的同时,并没有废弃面向对象

  4. Scala 既有动态语言那样的灵活简洁,同时有保留了静态类型检查的安全与执行效率

  5. Scala 既能处理脚本化的临时任务,又能处理高并发场景下的分布式互联网大数据应用,可谓能缩能伸

Chap 1 基础

focus on:

  1. 使用 scala 解释器

  2. 用 var 和 val 定义变量

  3. 数字类型

  4. 使用操作符和函数

  5. 浏览 Scaladoc

1.1 Scala解释器

scala> 8 * 5 + 2
res1: Int = 42scala> 0.5 * res1
res4: Double = 21.0scala> "Hello, " + res1
res5: String = Hello, 42
{ String = java.lang.String }

scala 程序并不是一个解释器。
输入的内容被快速地编译成字节码,然后这段字节码交由 Java 虚拟机执行。
我们称之为 : REPL

scala> res5.to  // tab 补全
toByte   toChar   toDouble   toFloat   toInt   toLong   toShort   toStringscala> res5.toUpperCase
res6: String = HELLO, 42

1.2 声明值和变量

Scala 鼓励使用 val, 除非你真的需要改变它。声明值和变量不初始化会报错。

注: 你不需要给出值和变量的类型,scala会根据初始化的值推断出来。

scala> val answer = 8 * 5 + 2
answer: Int = 42scala> answer * 0.5
res8: Double = 21.0

在必要的时候,你也可是指定类型

scala> val greeting: String = null
greeting: String = null
scala> val greeting: Any = "Hello"
greeting: Any = Helloscala> 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 = 1scala> 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 = 1234567890scala> 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.4142135623730951scala> pow(2, 4)
res16: Double = 16.0scala> 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 = 12345scala> 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 = HelLoscala> 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:

  1. Scala 中,几乎所有的构造出来语法结构都有值。(区别于Java语句没有值)

  2. if、块、 表达式 有值

  3. void 类型是 Unit

  4. 避免在函数定义中使用 return

  5. 注意别在函数式定义中漏掉了=。

  6. Scala 没有受检异常

2.1 条件表达式

scala> var x = -4
x: Int = -4scala> val s = if (x > 0) 1 else -1
s: Int = -1scala> 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.605551275463989scala> x = y = 1
<console>:9: error: type mismatch;found   : Unitrequired: Intx = y = 1^
{注意 : 赋值语句的值是 Unit}

2.3 输入和输出

scala> print("Answer: ")
Answer:
scala> print(42)
42
scala> println("Answer : " + 42)
Answer : 42scala> // 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 = Beanscala> val age = readInt()
warning: there was one deprecation warning; re-run with -deprecation for details
age: Int = 25readDouble、readByte、readShort、readLong、
readFloat、readBoolean、readChar。

2.4 循环

scala> var n = 3
n: Int = 3
scala> var r = 1
r: Int = 1scala> :paste
// Entering paste mode (ctrl-D to finish)
while (n > 0) {r = r * nn -= 1
}
// Exiting paste mode, now interpreting.scala> print(n)
0
scala> print(r)
6
scala> var n = 3
n: Int = 3scala> 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 = Helloscala> var sum = 0
sum: Int = 0scala> for (i <- 0 until s.length)| sum += s(i)scala> sum
res7: Int = 500scala> var sum = 0
sum: Int = 0你可以直接遍历字符序列,不需要使用下标scala> for (ch <- "Hello") sum += chscala> 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 = HIeflmlmopscala> 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)Doublescala> abs(-5)
res22: Double = 5.0scala> def fac(n: Int) = {| var r = 1| for (i <- 1 to n) r = r * i| r| }
fac: (n: Int)Intscala> 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)Intscala> 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)Stringscala> 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*)Intscala> 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.Inclusiverequired: Intval 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*)Intscala> 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 = -1scala> 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:

  1. 若 长度固定 则使用 Array, 若长度可能有变化则使用 ArrayBuffer。

  2. 提供初始值时不要使用 new

  3. 用 () 来访问元素

  4. 用 for (elem <- arr) 来遍历元素

  5. 用 for (elem <- arr if ...)...yield... 原数组转型为新数组

  6. 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.ArrayBufferscala> 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 = 7scala> 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).reversefor (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 samescala> Array(1, 7, 2).min
res1: Int = 1scala> Array(1, 7, 2).max
res2: Int = 7scala> Array(1, 7, 2.5).max
res4: Double = 7.0scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBufferscala> 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 = 1279scala> a.mkString(",")
res13: String = 1,2,7,9scala> 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) = 42scala> 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.ArrayBufferscala> 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.asScalaBufferscala> 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:

  1. Scala 有十分易用的语法来创建、查询、遍历映射

  2. 你需要从可变和不可变的映射中做出选择

  3. 默认你得到的是 哈希映射, 你也可以指明要 树形映射

  4. Scala映射 和 Java映射 之间来回切换

  5. 元组可以用来聚集值

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 = 3scala> val bobsScore = if (scores.contains("Bob")) scores("Bob") else 0
bobsScore: Int = 3scala> val bobsScore = if (scores.contains("Bob")) scores("Bobo") else 0
java.util.NoSuchElementException: key not found: Boboat 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 elidedscala> val bobsScore = if (scores.contains("Bobo")) scores("Bob") else 0
bobsScore: Int = 0scala> 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")=10scala> val bobsScore = scores.getOrElse("Bob", 0)
bobsScore: Int = 10scala> scores("Kevin")=100scala> 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.mapAsScalaMapscala> 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.propertiesAsScalaMapscala> 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.14scala> 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)

Scala Learn 1 Basic相关推荐

  1. how to learn html5,HTML5与CSS基础

    你将学到什么 How to write a Web page Concepts of a markup language Basics of HTML5 and CSS Web design and ...

  2. scala代码示例_Scala集合示例

    scala代码示例 Scala Collections are the containers that hold sequenced linear set of items like List, Se ...

  3. 3000门徒内部训练绝密视频(泄密版)第5课:彻底精通Scala隐式转换和并发编程及Spark源码阅读

    彻底精通Scala隐式转换和并发编程及Spark源码阅读 Akka ,Scala内部并发 隐式转换.隐式类.隐式参数 可以手动指定某种类型的对象或类转换成其他类型的对象或类.转换的原因是假设写好接口 ...

  4. 播客#47:劳伦斯·布拉德福德

    On today's episode, I interview Laurence Bradford. She's the creator of the LearnToCodeWith.me blog ...

  5. dsp28335读地址c语言写法,dsp 28335 教程 附录4 dsp的c语言.ppt

    您所在位置:网站首页 > 海量文档 &nbsp>&nbsp计算机&nbsp>&nbsp嵌入式开发 dsp 28335 教程 附录4 dsp的c语言.p ...

  6. a byte of python-A Byte of Python PDF 下载

    主要内容: 1 A Byte of Python "A Byte of Python' is a free book on programming using the Python lang ...

  7. python在会计中的应用-Python会计数据分析

    你将学到什么 Know how to operate software that will help you create and run Python code. Execute Python co ...

  8. python土木_土木和结构工程师用Python-Python for civil and structural engineers

    标题(title):Python for civil and structural engineers 土木和结构工程师用Python 作者(author):Vittorio Lora 出版社(pub ...

  9. systemverilog硬件设计及建模_Chisel引领敏捷硬件开发浪潮

    转载一篇18年6月的旧文 众所周知,近来开源处理器项目RISC-V在半导体业界掀起了一片新的潮流.这股潮流同时带来的,还包括了敏捷芯片开发. "敏捷开发"对于IC设计工程师来说似乎 ...

最新文章

  1. DIV与Table布局在大型网站的可用性比较
  2. 10分钟,快速使用VUE-VUEX
  3. Angular.js中使用$watch监听模型变化
  4. hdu 1166 敌兵布阵 树状数组 模板题
  5. JavaScript 详说事件机制之冒泡、捕获、传播、委托
  6. 工作179:接口对接
  7. 使用GNOME桌面工具管理Linux(4)–服务的使用
  8. 数据结构之栈与队列(二)
  9. 购物车及商品php代码_简单的php购物车代码
  10. mysql8 groups关键字
  11. 为什么我们应该学马化腾,而不是马云
  12. 【声源定位】基于matlab广义互相关声源定位【含Matlab源码 548期】
  13. TextView属性设置
  14. python 股票回测系统_Python爬虫回测股票的实例讲解
  15. 一起学爬虫(Python) — 23 自动化详解2
  16. EXCEL地理工具--小O地图EXCEL插件0705版 2022.4.28发布
  17. Splunk中12小时制AM/PM的日期转换
  18. 中国古代数学家张丘建在他的《算经》中提出了著名的“百钱买百鸡问题”:鸡翁一,值钱五,鸡母一,值钱三,鸡雏三,值钱一,百钱买百鸡,问翁、母、雏各几何?
  19. win10亮度调节消失的解决方法
  20. Python 李查逊/Richardson加速外推法

热门文章

  1. Google 全球 IP 地址库一览表(更新中)
  2. android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu[转]
  3. Java单元测试与Jutil详解(一) 简介
  4. Android 设计模式:(三)装饰者模式 —— 装饰对象
  5. Python-socket编程
  6. STC12C5A60S2 内部AD+1602显示
  7. ASP.NET MVC 5 - 视图
  8. Sql server 阻塞定位
  9. Cisco交换机解决网络蠕虫病毒***问题
  10. Infragistics NetAdvantage 2006 Volume 2 CLR 2.0曲折安装