大数据
流式处理

Scala快速入门

简介:Scala快速入门

1. lazy使用

lazy关键字用于延迟加载某些变量,被lazy修饰的内容将会在使用的时候被加载:

  • scala> lazy var i = 1
  • <console>:1: error: lazy not allowed here. Only vals can be lazy
  • lazy var i = 1
  • ^
  • scala> lazy val i = 1
  • i: Int = <lazy>

需要注意的是var变量不能使用lazy修饰。

lazy一般用于打开某些资源的情况,比如要打开一个文件,但并不是立即就要使用该文件的内容,就可以使用lazy来修饰打开文件的操作:

  • // 直接加载的方式
  • scala> fromFile("d:/data/word").mkString
  • res0: String =
  • hello world
  • // 使用lazy延迟加载的方式
  • scala> lazy val file = fromFile("d:/data/word").mkString
  • file: String = <lazy>
  • scala> file
  • res1: String =
  • hello world tom

需要注意的是,lazy由于是延迟加载,所以延迟加载的内容在编译时并不会检查,因此在运行可能会由于语法错误报错,例如上面的例子中,d:/data/word文件不存在的话,并不会报错,只有在真正使用的时候才会报错:

  • scala> lazy val file1 = fromFile("d:/data/word1").mkString
  • file1: String = <lazy>
  • scala> file1
  • java.io.FileNotFoundException: d:\data\word1 (系统找不到指定的文件)
  • at java.io.FileInputStream.open0(Native Method)
  • at java.io.FileInputStream.open(FileInputStream.java:195)
  • at java.io.FileInputStream.<init>(FileInputStream.java:138)
  • at scala.io.Source$.fromFile(Source.scala:91)
  • at scala.io.Source$.fromFile(Source.scala:76)
  • at scala.io.Source$.fromFile(Source.scala:54)
  • at .file1$lzycompute(<console>:14)
  • at .file1(<console>:14)
  • ... 29 elided

2. 使用scala-library库

可以直接使用Maven提供的scala-library进行Scala代码的编写:

  • <dependency>
  • <groupId>org.scala-lang</groupId>
  • <artifactId>scala-library</artifactId>
  • <version>${scala.version}</version>
  • </dependency>

3. 函数相关

函数的定义格式如下:

  • def FunctionName(ParameterName: ParameterType): ReturnType = {
  • // Function Body
  • }

例如下面的函数:

  • def add(param1: Int, param2: Int): Int = {
  • param1 + param2
  • }

Scala中,函数返回值不需要return关键字。

没有参数的函数在调用时可以不加()

  • def main(args: Array[String]): Unit = {
  • sayHello // 与下面的调用效果一样
  • sayHello()
  • }
  • def sayHello(): Unit = {
  • println("Hello")
  • }

3.1. 默认参数

Scala可以给参数一个默认值,不传该参数时使用默认值:

  • def main(args: Array[String]): Unit = {
  • welcome() // 输出welcome
  • welcome("Hello Welcome") // 输出Hello Welcome
  • }
  • def welcome(slogan: String = "welcome"): Unit = {
  • println(slogan)
  • }

3.2. 命名参数

Scala可以给传入的参数指定名称:

  • def main(args: Array[String]): Unit = {
  • println(fullName("Jack", "Chen"))
  • println(fullName(SecondName = "Chen", firstName = "Jack")) // 与上面的调用效果一样
  • }
  • def fullName(firstName: String, SecondName: String): String = {
  • firstName + " " + SecondName
  • }

3.3. 可变参数

Scala可以传入不定数量的可变参数:

  • def main(args: Array[String]): Unit = {
  • println(add(1, 2, 3)) // 6
  • println(add(4, 5, 6, 7, 8, 9, 10)) // 49
  • }
  • def add(numbers: Int*): Int = {
  • var sum = 0
  • for (number <- numbers) {
  • sum = sum + number
  • }
  • sum
  • }

3.4. 循环范围

  • scala> 1 to 10
  • res1: scala.collection.immutable.Range.Inclusive = Range 1 to 10
  • scala> 1.to(10)
  • res2: scala.collection.immutable.Range.Inclusive = Range 1 to 10
  • scala> Range(1, 10)
  • res3: scala.collection.immutable.Range = Range 1 until 10
  • scala> Range(1, 10 ,2)
  • res4: scala.collection.immutable.Range = inexact Range 1 until 10 by 2
  • scala> 1 until 10
  • res5: scala.collection.immutable.Range = Range 1 until 10
  • scala> 1.until(10)
  • res6: scala.collection.immutable.Range = Range 1 until 10

for循环格式:

  • for (i <- 1 to 9) {
  • print(i) // 123456789,左闭右闭
  • }
  • for (i <- 1.to(9)) {
  • print(i) // 123456789
  • }
  • for (i <- 1 until 9) {
  • print(i) // 12345678,左闭右开
  • }
  • for (i <- 1.until(9)) {
  • print(i) // 12345678
  • }

foreach循环:

  • val numbers = 1.to(9)
  • numbers.foreach(number => print(number)) // 123456789

where循环

  • var index = 0
  • var sum = 0
  • while(index <= 10) {
  • sum = sum + index
  • index = index + 1
  • }
  • print(sum) // 55

4. 面向对象

4.1. 类的定义

class关键字用于定义一个类:

  • package com.coderap
  • object SimpleApp {
  • def main(args: Array[String]): Unit = {
  • val person = new Person()
  • println("name: " + person.name + ", age: " + person.age) // name: null, age: 0
  • person.name = "Jack"
  • person.age = 20
  • println("name: " + person.name + ", age: " + person.age) // name: Jack, age: 20
  • println(person.run()) // Jack is running
  • println(person.run) // Jack is running
  • person.say("Hello") // Jack said Hello
  • person.setSalary(2000)
  • println(person.getSalary()) // 2000.0
  • // println(person.salary) // 无法访问
  • }
  • }
  • // 定义一个Person类
  • class Person {
  • // 定义属性
  • var name: String = _ // "_"是占位符,会取默认值,使用占位符必须要有类型说明
  • var age: Int = _
  • // 私有属性
  • private[this] var salary: Double = _
  • // 类内部访问私有属性
  • def getSalary(): Double = {
  • salary
  • }
  • def setSalary(salary: Double) = {
  • this.salary = salary
  • }
  • // 定义方法
  • def run(): String = {
  • name + " is running"
  • }
  • def say(words: String) = {
  • println(name + " said " + words)
  • }
  • }

4.2. 类的构造器

  • package com.coderap.construct
  • object ClassConstructorTest {
  • def main(args: Array[String]): Unit = {
  • val jack = new Person("Jack", 20)
  • println("name: " + jack.name + ", age: " + jack.age) // name: Jack, age: 20
  • val marry = new Person("Marry", 18, "F")
  • println("name: " + marry.name + ", age: " + marry.age + ", gender: " + marry.gender) // name: Marry, age: 18, gender: F
  • }
  • }
  • // 主构造器
  • class Person(val name: String, val age: Int) {
  • var gender: String = _
  • // 附属构造器
  • def this(name: String, age: Int, gender: String) {
  • // 附属构造器的第一行代码必须要调用主构造器或者其他附属构造器
  • this(name, age)
  • this.gender = gender
  • }
  • // 私有构造器
  • private[this] def this() {
  • this("no-name", 0)
  • }
  • }

4.3. 继承

  • package com.coderap.construct
  • object ClassConstructorTest {
  • def main(args: Array[String]): Unit = {
  • val student = new Student("Tom", 20, "CS")
  • println("name: " + student.name + ", age: " + student.age + ", major: " + student.major)
  • }
  • }
  • // 主构造器
  • class Person(val name: String, val age: Int) {
  • var gender: String = _
  • val school: String = "Peking"
  • // 附属构造器
  • def this(name: String, age: Int, gender: String) {
  • // 附属构造器的第一行代码必须要调用主构造器或者其他附属构造器
  • this(name, age)
  • this.gender = gender
  • }
  • // 私有构造器
  • private[this] def this() {
  • this("no-name", 0)
  • }
  • }
  • class Student(name: String, age: Int, val major: String) extends Person(name, age) {
  • // 重写属性,需要注意的是,只能重写val属性,不可重写var属性
  • override val school: String = "Beijing"
  • // 重写方法
  • override def toString: String = {
  • "This is override toString function of Student"
  • }
  • }

注意继承的时候这一行class Student(name: String, age: Int, val major: String) extends Person(name, age),其中val major: String表示,如果我们需要让传入的构造器的属性在外部可见,需要加上varval修饰符,从父类继承的可以不加,但新增的属性想要在外部可见一定要加修饰符。

在使用子类实例化对象时,会先调用父类的构造器,再调用子类的构造器。

4.4. 抽象类

  • package com.coderap.abstracttest
  • object AbstractClass {
  • def main(args: Array[String]): Unit = {
  • val student = new Student()
  • student.speak // Student speak
  • println("name: " + student.name + ", age: " + student.age) // name: Jack, age: 20
  • }
  • }
  • // 抽象类
  • abstract class Person {
  • // 抽象方法
  • def speak
  • // 抽象属性
  • var name: String
  • var age: Int
  • }
  • // 继承类
  • class Student extends Person {
  • // 重写抽象方法
  • override def speak: Unit = {
  • println("Student speak")
  • }
  • // 重写抽象属性
  • override var name: String = "Jack"
  • override var age: Int = 20
  • }

4.5. 伴生类和伴生对象

  • package com.coderap.apply
  • object ApplyTest {
  • }
  • /**
  • * 如果一个class有一个与之同名的object
  • * 那么就称这个object是class的半生对象,class是object的伴生类
  • */
  • // 这是object Apply的半生类
  • class Apply {
  • }
  • // 这是class Apply的半生对象
  • object Apply {
  • }

4.6. apply

对于apply方法谨记两点:

  1. 类名()调用的是object的apply方法,对象()调用的是class的apply方法;
  2. 一般最佳实践实在object的apply方法里直接返回new好的class对象。
  • package com.coderap.apply
  • object ApplyTest {
  • def main(args: Array[String]): Unit = {
  • for (i <- 1 to 10) {
  • Apply.increment
  • }
  • println(Apply.i) // 10,说明object本身就是一个单例对象
  • var apply1 = Apply() // object apply invoked,表名调用的是object的apply方法
  • apply1.apply // class apply invoked,表名调用的是class的apply方法
  • }
  • }
  • class Apply {
  • def apply() = {
  • println("class apply invoked")
  • }
  • }
  • object Apply {
  • var i: Int = _
  • def increment(): Unit = {
  • i = i + 1
  • }
  • // 一般最佳实践就是在object的apply方法里直接返回一个`new Apply`
  • def apply(): Apply = {
  • println("object apply invoked")
  • new Apply
  • }
  • }

4.7. case class样例类

case class和普通的class其实是一样的,唯一的区别是case class在创建的时候不用new,case class一般用于模式匹配。

  • package com.coderap.caseclass
  • object CaseClassTest {
  • def main(args: Array[String]): Unit = {
  • println(Person("Tom").name) // Tom
  • }
  • }
  • // case class在创建的时候不用new,一般用在模式匹配中
  • case class Person(name: String)

4.8. Trait

trait是一个抽象类。

  • trait Cloneable extends java.lang.Object with java.lang.Cloneable {
  • ...
  • }
  • private[spark] trait Logging {
  • ...
  • }
  • trait Serializable extends scala.Any with java.io.Serializable {
  • ...
  • }
  • class SparkConf(loadDefaults: Boolean) extends Cloneable with Logging with Serializable {
  • ...
  • }

在继承多个trait时第一个使用extends关键字,后面的都使用with关键字。

5. 集合

继承自App的object不需要main方法可以直接运行:

  • object ArrayApp extends App {
  • println("hello")
  • }

5.1. 定长数组

  • // 使用new创建
  • scala> val a = new Array[String](5)
  • a: Array[String] = Array(null, null, null, null, null)
  • scala> a.length
  • res10: Int = 5
  • scala> a(0) = "hello"
  • scala> a(0)
  • res12: String = hello
  • // 使用apply直接创建
  • scala> val b = Array("hello", "world")
  • b: Array[String] = Array(hello, world)
  • scala> b
  • res14: Array[String] = Array(hello, world)

object Array的apply源码如下:

  • def apply[T: ClassTag](xs: T*): Array[T] = {
  • val array = new Array[T](xs.length)
  • var i = 0
  • for (x <- xs.iterator) { array(i) = x; i += 1 }
  • array
  • }

一些快捷的方法:

  • scala> val c = Array(1, 2, 3, 4, 5, 6)
  • c: Array[Int] = Array(1, 2, 3, 4, 5, 6)
  • // 求和
  • scala> c.sum
  • res15: Int = 21
  • // 最大值
  • scala> c.max
  • res16: Int = 6
  • // 最小值
  • scala> x.min
  • res17: Int = 1
  • // 转换为String
  • scala> c.mkString
  • res18: String = 123456
  • // 转换为String,中间有分隔符
  • scala> c.mkString(",")
  • res19: String = 1,2,3,4,5,6
  • // 转换为String,两边有包裹符
  • scala> c.mkString("<", ",", ">")
  • res20: String = <1,2,3,4,5,6>

5.2. 可变数组

  • scala> val mA = scala.collection.mutable.ArrayBuffer[Int]()
  • mA: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
  • // 添加元素(+=)
  • scala> mA += 1
  • res22: mA.type = ArrayBuffer(1)
  • scala> mA += 2
  • res23: mA.type = ArrayBuffer(1, 2)
  • scala> mA
  • res24: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2)
  • // 添加多个元素(+=)
  • scala> mA += (3, 4, 5)
  • res25: mA.type = ArrayBuffer(1, 2, 3, 4, 5)
  • scala> mA
  • res26: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5)
  • // 从Array中添加元素(++=)
  • scala> mA ++= Array(6, 7, 8)
  • res28: mA.type = ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)
  • // 在第三个位置添加一个5,insert声明:insert(n: Int, elems: A*)
  • scala> mA.insert(3, 5)
  • scala> mA
  • res34: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 5, 4, 5, 6, 7, 8)
  • // 移除第三个元素
  • scala> mA.remove(3)
  • res35: Int = 5
  • scala> mA
  • res36: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)
  • // 移除从0开始的两个元素
  • scala> mA.remove(0, 2)
  • scala> mA
  • res38: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 4, 5, 6, 7, 8)
  • // 删除末尾3个元素
  • scala> mA.trimEnd(3)
  • scala> mA
  • res40: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 4, 5)
  • // 可变转不可变
  • scala> val d = mA.toArray
  • d: Array[Int] = Array(3, 4, 5)

5.3. 数组遍历

  • // 正序遍历
  • for (i <- 0 until mA.length) {
  • println(mA(i))
  • }
  • // 逆序遍历
  • for (i <- (0 until mA.length).reverse) {
  • println(mA(i))
  • }
  • // 正序遍历
  • for (ele <- mA) {
  • println(ele)
  • }

5.4. List

  • // Nil其实就是一个空List
  • scala> Nil
  • res41: scala.collection.immutable.Nil.type = List()
  • // 创建List
  • scala> val l1 = List(1, 2, 3, 4, 5)
  • l1: List[Int] = List(1, 2, 3, 4, 5)
  • // List的头是第一个元素
  • scala> l1.head
  • res42: Int = 1
  • // List的tail是除去head剩下的元素
  • scala> l1.tail
  • res43: List[Int] = List(2, 3, 4, 5)
  • // l1作为head,Nil作为tail
  • scala> val l2 = l1 :: Nil
  • l2: List[List[Int]] = List(List(1, 2, 3, 4, 5))
  • // 数字1作为head,l1作为tail
  • scala> val l3 = 1 :: l1
  • l3: List[Int] = List(1, 1, 2, 3, 4, 5)
  • // 需要接上Nil,否则会报错
  • scala> val l4 = 1 :: 2 :: 3
  • <console>:14: error: value :: is not a member of Int
  • val l4 = 1 :: 2 :: 3
  • ^
  • scala> val l4 = 1 :: 2 :: 3 :: Nil
  • l4: List[Int] = List(1, 2, 3)

5.5. 可变List

  • scala> var l5 = scala.collection.mutable.ListBuffer[Int]()
  • l5: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
  • // 添加元素
  • scala> l5 += 2
  • res44: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2)
  • // 添加多个元素
  • scala> l5 += (3, 4, 5)
  • res45: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3, 4, 5)
  • // 从List中添加元素
  • scala> l5 ++= List(6, 7, 8)
  • res46: scala.collection.mutable.ListBuffer[Int] = ListBuffer(2, 3, 4, 5, 6, 7, 8)
  • // 删除元素
  • scala> l5 -= 2
  • res47: scala.collection.mutable.ListBuffer[Int] = ListBuffer(3, 4, 5, 6, 7, 8)
  • // 删除多个元素,删除的元素不存在也不会报错
  • scala> l5 -= (1, 4)
  • res48: scala.collection.mutable.ListBuffer[Int] = ListBuffer(3, 5, 6, 7, 8)
  • // 转为不可变List
  • scala> l5.toList
  • res49: List[Int] = List(3, 5, 6, 7, 8)
  • // 转为数组
  • scala> l5.toArray
  • res50: Array[Int] = Array(3, 5, 6, 7, 8)
  • // 判断是否为空
  • scala> l5.isEmpty
  • res51: Boolean = false
  • // head
  • scala> l5.head
  • res52: Int = 3
  • // tail
  • scala> l5.tail.head
  • res53: Int = 5

使用head和tail递归求和:

  • def sum(nums: Int*): Int = {
  • if (nums.length == 0) {
  • 0
  • } else {
  • nums.head + sum(nums.tail: _*)
  • }
  • }

nums.tail: _*可以把集合转为可变参数。

5.6. Set

  • scala> val s1 = Set(1, 2, 2, 4, 4, 5, 6, 3, 2)
  • s1: scala.collection.immutable.Set[Int] = Set(5, 1, 6, 2, 3, 4)

5.7. Map

  • // Map 键值对演示
  • scala> colors
  • res54: scala.collection.immutable.Map[String,String] = Map(red -> #FF0000
  • F0FFFF)
  • // 所有key
  • scala> colors.keys
  • res56: Iterable[String] = Set(red, azure)
  • // 所有value
  • scala> colors.values
  • res57: Iterable[String] = MapLike.DefaultValuesIterable(#FF0000, #F0FFFF)

5.8. 可变Map

  • scala> val colors = scala.collection.mutable.Map[String,String]()
  • colors: scala.collection.mutable.Map[String,String] = Map()
  • // 添加键值对
  • scala> colors += ("green" -> "#00FF00")
  • res70: colors.type = Map(green -> #00FF00)
  • scala> colors += ("yellow" -> "#FFFF00")
  • res71: colors.type = Map(yellow -> #FFFF00, green -> #00FF00)
  • scala> colors
  • res72: scala.collection.mutable.Map[String,String] = Map(yellow -> #FFFF00, green -> #00FF00)
  • scala> colors.keys
  • res73: Iterable[String] = Set(yellow, green)
  • scala> colors.values
  • res74: Iterable[String] = HashMap(#FFFF00, #00FF00)
  • // 判断是否为空
  • scala> colors.isEmpty
  • res75: Boolean = false
  • scala> val colors_copy = Map("blue" -> "#0000FF", "yellow" -> "#FFEE00")
  • colors_copy: scala.collection.immutable.Map[String,String] = Map(blue -> #0000FF, yellow -> #FFEE00)
  • // 将两个Map合在一起,++作为运算符,Map合并时会移除重复的key,后面覆盖前面的
  • scala> colors ++ colors_copy
  • res76: scala.collection.mutable.Map[String,String] = Map(yellow -> #FFEE00, green -> #00FF00, blue -> #0000FF)
  • // 将两个Map合在一起,++作为函数
  • scala> colors.++(colors_copy)
  • res77: scala.collection.mutable.Map[String,String] = Map(yellow -> #FFEE00, green -> #00FF00, blue -> #0000FF)
  • // 判断是否存在某个key
  • scala> colors.contains("yellow")
  • res78: Boolean = true

5.9. Option、Some、None

为了让所有东西都是对象的目标更加一致,也为了遵循函数式编程的习惯,Scala鼓励你在变量和函数返回值可能不会引用任何值的时候使用Option类型。在没有值的时候,使用None,这是Option的一个子类。如果有值可以引用,就使用Some来包含这个值。Some也是Option的子类。
None被声明为一个对象,而不是一个类,因为我们只需要它的一个实例。这样,它多少有点像null关键字,但它却是一个实实在在的,有方法的对象。

Option类型的值通常作为Scala集合类型(List,Map等)操作的返回类型。比如Map的get方法:

  • scala> val capitals = Map("France"->"Paris", "Japan"->"Tokyo", "China"->"Beijing")
  • capitals: scala.collection.immutable.Map[String,String] = Map(France -> Paris, Japan -> Tokyo, China -> Beijing)
  • scala> capitals get "France"
  • res0: Option[String] = Some(Paris)
  • scala> capitals get "North Pole"
  • res1: Option[String] = None

Option有两个子类别,Some和None。当程序回传Some的时候,代表这个函式成功地给了你一个String,而你可以透过get()函数拿到那个String,如果程序返回的是None,则代表没有字符串可以给你。

在返回None,也就是没有String给你的时候,如果你还硬要调用get()来取得 String 的话,Scala一样是会抛出一个NoSuchElementException异常给你的。

我们也可以选用另外一个方法,getOrElse。这个方法在这个Option是Some的实例时返回对应的值,而在是None的实例时返回传入的参数。换句话说,传入getOrElse的参数实际上是默认返回值。

  • scala> capitals get "North Pole" get
  • warning: there was one feature warning; re-run with -feature for details
  • java.util.NoSuchElementException: None.get
  • at scala.None$.get(Option.scala:347)
  • at scala.None$.get(Option.scala:345)
  • ... 33 elided
  • scala> capitals get "France" get
  • warning: there was one feature warning; re-run with -feature for details
  • res3: String = Paris
  • scala> (capitals get "North Pole") getOrElse "Oops"
  • res7: String = Oops
  • scala> capitals get "France" getOrElse "Oops"
  • res8: String = Paris

通过模式匹配分离可选值,如果匹配的值是Some的话,将Some里的值抽出赋给x变量:

  • def showCapital(x: Option[String]) = x match {
  • case Some(s) => s
  • case None => "?"
  • }

Scala程序使用Option非常频繁,在Java中使用null来表示空值,代码中很多地方都要添加null关键字检测,不然很容易出现NullPointException。因此Java程序需要关心那些变量可能是null,而这些变量出现null的可能性很低,但一但出现,很难查出为什么出现NullPointerException
Scala的Option类型可以避免这种情况,因此Scala应用推荐使用Option类型来代表一些可选值。使用Option类型,读者一眼就可以看出这种类型的值可能为None。

实际上,多亏Scala的静态类型,你并不能错误地尝试在一个可能为null的值上调用方法。虽然在Java中这是个很容易犯的错误,它在Scala却通不过编译,这是因为Java中没有检查变量是否为null的编程作为变成Scala中的类型错误(不能将Option[String]当做String来使用)。所以,Option的使用极强地鼓励了更加弹性的编程习惯。

详解Option[T]

在Scala里Option[T]实际上是一个容器,就像数组或是List一样,你可以把他看成是一个可能有零到一个元素的List。
当你的Option里面有东西的时候,这个List的长度是1(也就是 Some),而当你的Option里没有东西的时候,它的长度是0(也就是 None)。

for循环

如果我们把Option当成一般的List来用,并且用一个for循环来走访这个Option的时候,如果Option是None,那这个for循环里的程序代码自然不会执行,于是我们就达到了「不用检查Option是否为None这件事。

  • scala> val map1 = Map("key1" -> "value1")
  • map1: scala.collection.immutable.Map[String,String] = Map(key1 -> value1)
  • scala> val value1 = map1.get("key1")
  • value1: Option[String] = Some(value1)
  • scala> val value2 = map1.get("key2")
  • value2: Option[String] = None
  • scala> def printContentLength(x: Option[String]) {
  • | for (c <- x){
  • | println(c.length)
  • | }
  • | }
  • printContentLength: (x: Option[String])Unit
  • scala> printContentLength(value1)
  • 6
  • scala> printContentLength(value2)

map操作

在函数式编程中有一个核心的概念之一是转换,所以大部份支持函数式编程语言,都支持一种叫map()的动作,这个动作是可以帮你把某个容器的内容,套上一些动作之后,变成另一个新的容器。
现在我们考虑如何用Option的map方法实现length: xxx的输出形式:

先算出 Option 容器内字符串的长度
然后在长度前面加上 “length: ” 字样
最后把容器走访一次,印出容器内的东西

  • scala> value1.map(_.length).map("length: " + _).foreach(println)
  • length: 6
  • scala> value1.map("length: " + _.length).foreach(println)
  • length: 6

透过这样「转换」的方法,我们一样可以达成想要的效果,而且同样不用去做「是否为 None」的判断。

5.10. tuple

6. 模式匹配

  • // 普通模式匹配
  • def judgeGrade(grade: String) = {
  • grade match {
  • case "A" => println("A")
  • case "B" => println("B")
  • case "C" => println("C")
  • case "D" => println("D")
  • }
  • }
  • judgeGrade("C") // C
  • // 双重过滤
  • def judgeGrade(grade: String, name: String) = {
  • grade match {
  • case "A" => println("A")
  • case "B" => println("B")
  • case "C" => println("C")
  • case "D" => println("D")
  • case _ if (name == "Tom") => println("Tom")
  • }
  • }
  • judgeGrade("E", "Tom") // Tom
  • // 数组匹配
  • def greet(array: Array[String]): Unit = {
  • array match {
  • case Array("Jack") => println("hello, Jack")
  • case Array(x, y) => println("hello, " + x + ", " + y)
  • case Array("Jack", _*) => println("hello, Jack and other friends")
  • case _ => println("hello, everybody")
  • }
  • }
  • greet(Array("Jack")) // hello, Jack
  • greet(Array("Marry", "Tom")) // hello, Marry, Tom
  • greet(Array("Jack", "Tom", "Marry")) // hello, Jack and other friends
  • greet(Array("Tom", "Jack", "Marry")) // hello, everybody
  • // List匹配
  • def greet(list: List[String]): Unit = {
  • list match {
  • case "Jack" :: Nil => println("hello, Jack")
  • case x :: y :: Nil => println("hello, " + x + ", " + y)
  • case "Jack" :: tail => println("hello, Jack and other friends")
  • case _ => println("hello, everybody")
  • }
  • }
  • greet(List("Jack")) // hello, Jack
  • greet(List("Marry", "Tom")) // hello, Marry, Tom
  • greet(List("Jack", "Tom", "Marry")) // hello, Jack and other friends
  • greet(List("Tom", "Jack", "Marry")) // hello, everybody
  • // 类型匹配
  • def typeMatch(obj: Any): Unit = {
  • obj match {
  • case i: Int => println("Int: " + i)
  • case s: String => println("String: " + s)
  • case m: Map[_, _] => println("Map: " + m)
  • case _ => println("Other type: " + _)
  • }
  • }
  • typeMatch(1) // Int: 1
  • typeMatch("Hello") // String: Hello
  • typeMatch(Map("red" -> "#FF0000")) // Map: Map(red -> #FF0000)
  • typeMatch(1.0f) // com.coderap.matches.MatchApp$$$Lambda$13/1887813102@1ce92674

6.1. 异常使用模式匹配处理

  • try {
  • val i = 10 / 0
  • println(i)
  • } catch{
  • case e:ArithmeticException => println("ArithmeticException")
  • case e:Exception => println(e.getMessage)
  • } finally {
  • println("finally block invoked")
  • }

6.2. case class模式匹配

  • class Person
  • case class Man(name: String) extends Person
  • case class Woman(name: String) extends Person
  • case class Other(name: String) extends Person
  • def matchCaseClass(person: Person): Unit = {
  • person match {
  • case Man(name) => println("Man: " + name)
  • case Woman(name) => println("Woman: " + name)
  • case _ => println("Other")
  • }
  • }
  • matchCaseClass(Man("Jack")) // Man: Jack
  • matchCaseClass(Woman("Marry")) // Woman: Marry
  • matchCaseClass(Other("other")) // Other

6.3. Option模式匹配

  • val bigDataSkills =
  • Map("Java" -> "first",
  • "Hadoop" -> "second",
  • "Spark" -> "third",
  • "storm" -> "forth",
  • "hbase" -> "fifth",
  • "hive" -> "sixth",
  • "photoshop" -> null)
  • def show(value: Option[String]) = {
  • value match {
  • case Some(a) => a
  • case None => "No this Skill"
  • }
  • }
  • println(show(bigDataSkills.get("Java")) == "first") // true
  • println(show(bigDataSkills.get("photoshop")) == null) // true
  • println(show(bigDataSkills.get("Spark")) == "third") // true
  • println(show(bigDataSkills.get("abc123")) == "No this Skill") // true

7. 字符串操作

  • val slogan = "Hello"
  • val greet = "Tom"
  • // 高级字符串拼接,插值方式
  • println(s"$slogan: $greet")
  • // 多行字符串
  • val mutiLine =
  • """
  • |这是一个多行
  • |字符串
  • """.stripMargin
  • // 多行字符串插值
  • val slogan = "Hello"
  • val greet = "Tom"
  • val mutiLine =
  • s"""
  • |这是一个多行
  • |字符串
  • |$slogan
  • |$greet
  • """.stripMargin
  • val slogan = "Hello"

8. 匿名函数

  • // 将匿名函数传给一个常量
  • scala> val m1 = (x: Int) => x + 1
  • m1: Int => Int = $$Lambda$1270/1791637790@a081a15
  • scala> m1(10)
  • res89: Int = 11
  • // 将匿名函数传给一个函数
  • scala> def add = (x: Int, y: Int) => x + y
  • add: (Int, Int) => Int
  • scala> add(1, 2)
  • res90: Int = 3

9. 柯里化

  • def sum(a: Int, b: Int): Int = a + b
  • println(sum(1, 2))
  • // 柯里化
  • def sum2(a: Int)(b: Int): Int = a + b
  • println(sum2(1)(2))

10. 高阶函数

10.1. map

  • scala> val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
  • list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
  • scala> list.map((x: Int) => x + 1)
  • res91: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
  • scala> list.map((x) => x + 1)
  • res92: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
  • scala> list.map(x => x + 1)
  • res93: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
  • scala> list.map(_ + 1)
  • res94: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)

10.2. filter

  • scala> list.map(_ * 2).filter(_ > 10).foreach(println)
  • 12
  • 14
  • 16
  • 18

10.3. take

  • scala> list.take(3)
  • res96: List[Int] = List(1, 2, 3)

10.4. reduce、reduceLeft、reduceRight

  • scala> val list = List(1, 2, 3, 4, 5)
  • list: List[Int] = List(1, 2, 3, 4, 5)
  • scala> list.reduce(_ - _)
  • res98: Int = -13
  • scala> list.reduceLeft(_ - _)
  • res99: Int = -13
  • // (1 - ( 2 - ( 3 - ( 4 - 5 ))))
  • scala> list.reduceRight(_ - _)
  • res100: Int = 3

10.5. wordcount

  • val lines = List("hello tom hello jerry", "hello tom hello kitty hello china")
  • val wc = lines.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).map(t => (t._1, t._2.size)).toList.sortBy(_._2).reverse
  • val wc2 = lines.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.size)
  • val wc3 = lines.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.foldLeft(0)(_ + _._2))
  • println(wc) // List((hello,5), (tom,2), (jerry,1), (china,1), (kitty,1))
  • println(wc2) // Map(kitty -> 1, china -> 1, tom -> 2, hello -> 5, jerry -> 1)
  • println(wc3) // Map(kitty -> 1, china -> 1, tom -> 2, hello -> 5, jerry -> 1)