Scala必知必会

Scala必知必会

基本概念

Scala是一门多范式的编程语言。
设计初衷是要集成面向对象编程函数式编程的各种特性。
Scala运行在JAVA虚拟机上,并兼容现有的JAVA程序。
Scala源代码被编程成JAVA字节码,所以他可以运行在JVM上,并可以调用现有的JAVA类库。

变量和常量

常量:在程序运行过程中,其值不会被改变。
变量:在程序运行过程中,其值可以发生改变。

1
2
3
// 基本语法
val 常量名:常量类型 = 初始值
var 变量名:变量类型 = 初始值

注意:

  • 能使用常量的地方尽量不要使用变量
  • 声明变量时,类型可以省略。编译器自动推导
  • 类型确定后,不能修改(Scala是强数据类型语言)
  • 变量声明时,必须要有初始值
1
2
3
4
5
6
7
8

// Scala方法基本格式:def 方法名称(参数名称:参数类型):返回值类型={方法体}
def main(args: Array[String]): Unit = {
// 对象里面的参数可变。例如对象 class Student(name:String,var age:Int)
val tom = new Student("Tom", 20)
// 前提:必须要将属性声明成var,否则直接编译报错。
tom.age = 21
}

数据类型

Scala中一切数据都是对象,都是Any的子类。
Scala中数据类型分为两大类:数值类型(AnyVal)和引用类型(AnyRef),不管是数值类型还是引用类型都是对象。
Scala中数据类型遵守低精度的值类型向高精度的值类型自动转换(隐式转换)。

数值类型

基本类型

  • 整数类型:Byte、Short、Int、Long
  • 浮点类型:Float、Double
  • 字符类型:Char
  • 布尔类型:Boolean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1.Byte类型
val a: Byte = 12
// 2.Short类型
val b: Short = 12
// 3.Int类型
val c: Int = 12

// 注意:如果不声明类型,默认是Int类型
val d = 12

// 4.Long类型
val e: Long = 12
// 5.Float类型
val f: Float = 1.25f
// 6.Double类型
val g: Double = 1.25

// 注意:如果不声明类型,默认是Double类型
val h = 1.25

// 7.Char类型
val i: Char = 12
// 8.Boolean类型
val j: Boolean = true

Unit类型

对应Java中的void,用于方法返回值的位置,表示方法没有返回值。Unit只有一个实例值,写作()。

1
2
3
4
5
6
7
8
9
10
  def main(args: Array[String]): Unit = {
def m1():Unit={
println("mmm")
}
val m: Unit = m1
println(m)
}
// 结果输出
mmm
()

String Ops类型

是对Java中String类型的增强。

引用类型

Null类型

只有一个实例,那就是null。它是所有引用类型(AnyRef)的子类。NULL可以赋值给任意引用类型,但是不能赋值给值类型。

1
2
3
4
5
6
7
  def main(args: Array[String]): Unit = {
var tom = new Student("Tom",20)
tom = null
println(tom)
}
// 结果输出
null

Nothing类型

Nothing类型:即不属于数值类型,也不属于引用类型。
它是所有数据类型的子类,主要用在一个函数没有明确返回值时使用,因为这样用户可以把抛出的返回值,返回给任何的变量或者函数。

1
2
3
4
5
def main(args: Array[String]): Unit = {
def test(): Nothing = {
throw new Exception
}
}

类型转换

  • 自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成 精度大的那种数据类型,然后再进行计算。
  • 把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
  • (byte,short)和 char 之间不会相互自动转换。
  • byte,short,char 他们三者可以计算,在计算时首先转换为 int 类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1.高精度转低精度自动类型转换
val a: Int = 2
val b: Float = a

// 2.低精度转高精度强制类型转换
val c: Double = 2.45
val d: Int = c.toInt

// 3.String类型和Int类型相互转换
val e: Int = 4
val f: String = e + ""

val g: String = "2"
val h: Int = g.toInt

val i:String = "2.0"
val j:Int = i.toFloat.toInt

运算符

==

在Java中,==是比较两个对象在内存中的地址,equals是比较字符串中所包含的内容是否相同。
在Scala中,==更加类似于Java中的equals,如果需要比较地址,可以使用eq方法。

1
2
3
4
5
6
7
8
9
def main(args: Array[String]): Unit = {
val s1 = "abc"
val s2 = new String("abc")
println(s1 == s2)
println(s1.eq(s2))
}
// 结果输出
true
false

++

在Scala中,没有++、–操作符,可以通过+=、-=来替代。

1
2
3
4
5
6
7
def main(args: Array[String]): Unit = {
var a = 2
a += 1
println(a)
a -= 3
println(a)
}

三元运算符

在Scala中,没有三元运算符,可以通过if else来替代。

1
2
3
4
5
6
def main(args: Array[String]): Unit = {
// 注意这里if else的简写
def test(): Int = {
return if (true) 1 else 0
}
}

流程控制

Switch分支结构

在Scala中没有Switch分支结构,而是使用模式匹配。

For循环

范围遍历

1
2
3
4
5
6
7
8
// [1,3]
for (i <- 1 to 3) {
println(i)
}
// [1,3)
for (j <- 1 until 3) {
println(j)
}

集合遍历

1
2
3
4
// 集合循环
for (i <- Array(1,3,5)) {
println(i)
}

循环守卫

循环守卫,即循环保护式,保护式为true则进入循环体,为false则跳过,类似于continue。

1
2
3
4
5
6
7
8
9
10
// 循环守卫
for (i <- 1 to 5 if i != 3) {
println(i)
}
// 等价于
for (i <- 1 to 5) {
if (i != 3) {
println(i)
}
}

循环步长

1
2
3
4
// 1 3 5
for (i <- 1 to 5 by 2) {
println(i)
}

循环嵌套

1
2
3
4
5
6
7
8
9
10
// 注意:用分号分割
for (i <- 1 to 3; j <- 1 to 3) {
println("i:" + i + " j:" + j)
}
// 等价于
for (i <- 1 to 3) {
for (j <- 1 to 3) {
println("i:" + i + " j:" + j)
}
}

循环引入变量

1
2
3
4
// 引入变量j,通过i来确定j
for (i <- 1 to 3; j = 10 - i) {
println("i:" + i + " j:" + j)
}

循环返回值

将遍历过程中处理的结果返回到一个新的Vector集合中,使用yield关键字。平时开发过程中很少使用。

1
2
3
// 结果输出:Vector(1, 2, 3)
val res: immutable.IndexedSeq[Int] = for (i <- 1 to 3) yield i
println(res)

循环中断

Scala中去掉了break和continue,使用breakable控制结构来实现break和continue功能。

1
2
3
4
5
6
7
8
9
// 注意:需要导入相关引用:import scala.util.control.Breaks
Breaks.breakable(
for (i <- 1 to 10) {
if (i == 4) {
Breaks.break()
}
println(i)
}
)

函数式编程

基本语法

1
2
3
4
5
6
7
8
9
10
/**
* def 定义函数的关键字
* test 函数名
* @param x 参数名
* @param y 参数名
* @return x+y 函数体
*/
def test(x: Int, y: Int): Int = {
return x + y
}

注意:
Scala函数中没有重载和重写的概念。

1
2
3
4
5
6
7
8
def test(x: Int): Int = {
return x + 1
}

// 如果再定义一个test函数,会直接编译报错。
def test(s: String): String = {
return s + "..."
}

Scala函数中可以嵌套定义。

1
2
3
4
5
6
7
8
9
def test01(x: Int): Int = {
def test02(s: String): String = {
return s + "..."
}

return x + 1
}
// test01可以正常调用。
println(test01(1))

函数参数

可变参数

1
2
3
4
5
6
7
8
9
10
11
   def test(s: String*): Unit = {
println(s)
}
// 可变参数可以输入0个或多个入参。
test()
test("abc")
test("abc", "def")
// 执行结果
List()
WrappedArray(abc)
WrappedArray(abc, def)

注意:如果参数列表中存在多个参数,那么可变参数一般放在最后。

参数默认值

1
2
3
4
5
6
7
def test(name: String, age: Int = 20): Unit = {
println(name + ": " + age)
}
// 如果参数传递了值,会覆盖默认值。
test("Tom", 18)
// 如果参数没有传递值,会使用默认值。
test("Jack")

注意:一般情况下将有默认值的参数放置在参数列表的后面。

带名参数

1
2
3
4
5
def test(name: String, age: Int = 20): Unit = {
println(name + ": " + age)
}
// 带名参数
test(name = "Lucy")

函数至简原则:能省则省

以一个基本sum函数为例。

1
2
3
def sum(x: Int, y: Int): Int = {
return x + y
}

return可以省略,Scala会使用函数体最后一行代码作为返回值。

1
2
3
4
def sum(x: Int, y: Int): Int = {
//省略return
x + y
}

如果函数体只有一行代码,可以省略花括号。

1
2
3
def sum(x: Int, y: Int): Int =
// 省略花括号
x + y

返回值类型如果可以推断出来,可以省略。(注意:如果有return,则不能省略返回值类型)

1
2
3
def sum(x: Int, y: Int) =
// 省略返回值类型
x + y

如果函数中明确声明了Unit,那么即使函数体中使用return关键字也不起作用。

1
2
3
4
5
def test(): Unit =
return "abc"
// 输出结果
print(test())
// 结果为()

Scala如果期望是无返回值,可以省略等号。

1
2
3
4
// 注意:这里花括号不能省略。
def test() {
print("abc")
}

如果函数无参,但是使用了def声明,那么调用时小括号可加可不加。

1
2
3
4
def test() = "abc"
// 小括号调用时带不带均可以
print(test())
print(test)

如果函数无参,那么小括号可以省略,调用时小括号必须省略。

1
2
3
def test = "abc"
// 调用时必须省略小括号
print(test)

如果不关心名称,只关心逻辑处理,那么函数名def可以省略(实际上就是匿名函数)。

1
2
3
4
5
6
7
def test(name: String) = {
print(name)
}
// 简化成
(name: String) => {
print(name)
}

高阶函数

函数作为值传递

1
2
3
4
5
6
7
8
def f1(name: String): String = {
println(name)
name + "..."
}

// 将函数作为值传递。注意格式
val f2 = f1 _
// 注意:这里不能写成 val s1 = f1("abc"),因为这是函数的调用。

函数作为参数传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义fun函数。
val fun = (name: String) => {
print(name)
}

// 定义f函数。func作为参数。入参和返回值类型与fun一致,否则无法将fun作为入参传递。
def f(func: String => Unit): Unit = {
func("abc")
}
// 将fun函数作为一个参数进行传递
f(fun)
// 或者直接调用匿名函数
f((name: String) => {
print(name)
})

函数作为函数返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 函数作为函数的返回值返回。
def f1(): Int => String = {
def f2(x: Int): String = {
return x.toString
}

f2 _
}

// 调用第一次得到f2
val fun = f1()
// 调用第二次得到f2具体的值
val res = fun(1)
println(res)
// 简写
print(f1()(2))

匿名函数

基本语法

没有名字的函数就是匿名函数。

1
2
3
(x: Int) => {
函数体
}

匿名函数至简原则:能省则省

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def f(func: String => Unit): Unit = {
func("abc")
}
// 调用匿名函数
f((name: String) => {
print(name)
})
// 参数的类型可以省略,会根据形参进行自动推导
f((name) => {
print(name)
})
// 类型省略之后,如果发现只有一个参数,则圆括号可以省略。
// 注意:如果没有参数或者参数超过1个,那么不能省略圆括号
f(name => {
print(name)
})
// 如果匿名函数的函数体只有一行,大括号可以省略
f(name => print(name))
// 如果参数只出现一次,则参数省略,且后面的参数可以用_代替。
f(print(_))
// 还可以继续简化:如果可以推断出当前传入的println是一个函数体,而不是调用语句,可以直接省略_
f(print)

实现:定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。
它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。函数 f2 接
收一个 Char 类型的参数,返回一个 Boolean 的值。要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def func(x: Int): String => (Char => Boolean) = {
def f1(str: String): Char => Boolean = {
def f2(c: Char): Boolean = {
if (x == 0 && str == "" && c == '0') false else true
}

f2 _
}

f1 _
}

// 函数简写
def func1(x: Int): String => (Char => Boolean) = {
str => c => if (x == 0 && str == "" && c == '0') false else true
}

// 柯里化
def func2(x: Int)(str: String)(c: Char) = {
if (x == 0 && str == "" && c == '0') false else true
}

闭包&函数柯里化

闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和它所处的环境,称为闭包。

1
2
3
4
5
6
7
8
9
10
11
12
def f1(x: Int): Int => Int = {
def f2(y: Int): Int = {
x + y
}

f2
}

println(f1(2)(3))

// 简写
def f3(x: Int): Int => Int = x + _

函数柯里化:把一个参数列表的多个参数,变成多个参数列表。函数柯里化一定存在闭包。

1
2
3
4
// 函数柯里化
def f1(x: Int)(y: Int): Int = x + y

println(f1(2)(3))

控制抽象

1
2
3
4
5
6
7
8
9
// 传值参数
def f1(x: Int): Unit = {
println(x)
}

// 传名参数。传递的不再是具体的值,而是代码块。
def f2(x: => Int): Unit = {
println(x)
}

惰性加载

当函数返回值被声明为lazy时,函数的执行将被推迟,直到首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。
注意:lazy不能修饰var类型的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def main(args: Array[String]): Unit = {
val res = sum(10, 3)
println("1.函数调用")
println("2.res输出结果:" + res)
}

def sum(x: Int, y: Int): Int = {
println("3.sum方法")
x + y
}

// 正常输出
3.sum方法
1.函数调用
2.res输出结果: 13

// 使用lazy关键字修饰之后输出
lazy val res = sum(10, 3)
// 输出结果
1.函数调用
3.sum方法
2.res输出结果: 13

面向对象

Scala的面向对象思想和Java的面向对象思想一致。

Scala包

基本语法:package 包名
Scala 有两种包的管理风格,一种方式和 Java 的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用“.”进行分隔以表示包的层级关系,如com.atguigu.scala。另一种风格,通过嵌套的风格表示层级关系,如下

1
2
3
4
5
6
7
// 特点:1.一个源文件中可以声明多个 packag;2.子包中的类可以直接访问父包中的内容,而无需导包
package com{
package atguigu{
package scala{
}
}
}

注意:
Scala 中的三个默认导入分别是

1
2
3
import java.lang._
import scala._
import scala.Predef._

构造器

基本语法:

1
2
3
4
5
6
7
class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}

说明:
主构造器的参数列表要放在类名的后面,和类名放在一起
val修饰的构造参数具有不可变性
var修饰的构造参数具有可变性
辅助构造器第一行必须先调用主构造器
构造器参数列表前面加private是指伴生对象的权限,只有伴生对象才能访问

继承和多态

Scala中是单继承

抽象属性和抽象方法

定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类
定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法

特质(trait)

Scala 语言中,采用特质 trait(特征)来代替接口的概念,也就是说,多个类具有相同
的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字 trait 声明。
基本语法:

1
2
3
4
5
trait 特质名 {

trait 主体

}

集合

Scala的集合有三大类:序列 Seq、集 Set、映射 Map。
所有的集合都扩展自 Iterable特质。
对于几乎所有的集合类,Scala 都同时提供了可变和不可变的版本,分别位于以下两个包:
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
在Scala中,不可变集合是指集合对象不可修改,每次修改都会返回一个新对象,而不会对原对象进行修改。类似于Java中的String。可变集合是指可以直接对原对象进行修改,而不会返回新的对象。类似于Java中的StringBuffer。
建议:在操作集合的时候,不可变用符号,可变用方法。

序列 Seq

Array数组

不可变Array
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 不可变数组
// 使用new关键字
val arr = new Array[Int](5)
// 或者在定义数组时,直接赋值
val arr1 = Array(1, 2)
// 数组基本操作
// 查询数组元素
println(arr(2))
// 重新赋值
arr(0) = 1
// 采用方法的形式给数组赋值。将索引为1的位置元素修改为3
arr.update(1, 3)
// 数组遍历
for (i <- arr) {
println(i)
}
// 或者调用方法
arr.foreach(println)
// 调用方法查看数组
println(arr.mkString(","))
// 增加数组元素:由于穿件的是不可变数组,增加元素其实就是产生新的数组。这里添加一个元素3
val newArr = arr :+ 3
println(newArr.mkString(","))
}
可变ArrayBuffer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 可变数组。[Any]存放任意数据类型。
val arr = ArrayBuffer[Any](1, 5, 3)
// 数组基本操作
// 查询数组元素
println(arr(0))
// 查看数组
println(arr.mkString(","))
// 末尾增加数组元素
arr.append("str")
// 指定位置增加
arr.insert(0, -1)
// 末尾追加
arr.+=("zz")
// 修改元素
arr.update(1, 4)
// 或者直接重新赋值修改
arr(2) = 5
println(arr.mkString(","))

不可变数组与可变数组转换:

1
2
3
// 注意:这里的返回结果才是需求。数组本身无变化。
arr1.toBuffer //不可变数组转可变数组
arr2.toArray //可变数组转不可变数组
多维数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 说明:二维数组中有三个一维数组,每个一维数组中有四个元素
val array = Array.ofDim[Int](3, 4)
// 赋值
array(0)(0) = 1
// 二维数组遍历,使用增强for循环遍历。注意:这里的i和j是元素,不是索引。
for (i <- array) {
for (j <- i) {
print(j + " ")
}
println()
}
// 或者根据索引遍历
for (i <- 0 until array.length) {
for (j <- 0 until array(0).length) {
print(array(i)(j) + " ")
}
println()
}
// 或者调用方法直接获取索引遍历
for (i <- array.indices) {
for (j <- array(0).indices) {
print(array(i)(j) + " ")
}
println()
}

List列表

List 默认为不可变集合。数据有序,可重复。

不可变List
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 初始化不可变List
val list = List(1, 3, 5, 7)
// 遍历List
for (i <- list) {
println(i)
}
// 添加元素。::的运算规则是从右到左
val newList = 9 :: list
println(newList.mkString(","))
newList.foreach(println)
// 创建不可变List
var list1 = 12 :: 13 :: 14 :: Nil
// List(12, 13, 14)
println(list1)
// list合并
var list2 = 1 :: 2 :: 3 :: Nil
// 注意:这里是三个冒号。或者直接使用++
var list3 = list1 ::: list2
println(list3)
可变ListBuffer
1
2
3
4
5
6
7
8
9
10
11
12
// 初始化可变ListBuffer
val list = ListBuffer(1, 3, 5, 7)
// 添加元素
list.append(9)
// 遍历元素
list.foreach(println)
// 修改元素值
list.update(0, 2)
println(list.mkString(","))
// 移除元素
list.remove(0)
println(list.mkString(","))

Set

Set默认是不可变集合。数据无序,不可重复。

不可变Set

1
2
3
4
5
6
7
8
// 这里初始化两个2不会报错,但是输出只有1 2 3
val set = Set(1, 2, 2, 3)
println(set.mkString(","))
// size 为 3
println(set.size)
for (i <- set) {
println(i)
}

可变mutable.Set

1
2
3
4
5
6
7
8
// 创建可变数组
val set = mutable.Set(1, 2, 5, 7)
// 添加元素
set.add(4)
println(set.mkString(","))
// 删除元素
set.remove(2)
println(set.mkString(","))

映射 Map

不可变Map

1
2
3
4
5
6
7
8
9
10
11
// 创建不可变Map
val map = Map(1 -> "Jack", 2 -> "Lucy", 3 -> "Tom")
// 遍历元素
for (key <- map.keys) {
// 注意:这里需要get两次
println("key:" + key + ",value:" + map.get(key).get)
}
// 循环遍历
map.foreach((kv) => {
println(kv)
})

可变Map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建可变Map
val map = mutable.Map(1 -> "Jack", 2 -> "Lucy", 3 -> "Tom")
// 遍历元素
for (key <- map.keys) {
// 注意:这里需要get两次
println("key:" + key + ",value:" + map.get(key).get)
}
// 添加元素
map.+=(4 -> "Jay")
// 循环遍历
map.foreach((kv) => {
println(kv)
})
// 删除kv
map.-=(1, 2)
map.foreach((kv) => {
println(kv)
})

元组

元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
注意:元组中最大只能有22个元素。
Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val tuple: (Int, Boolean, String) = (10, true, "hi")
// 访问元组
println(tuple._1)
println(tuple._2)
// 遍历元组
for (i <- tuple.productIterator) {
println(i)
}
// 通过索引访问元祖
println(tuple.productElement(0))
// 元组嵌套
val tuple1 = (1, "hi", (2, "scala", false), true)
// 访问scala
println(tuple1._3._2)

队列

1
2
3
4
5
6
val queue = mutable.Queue[String]()
queue.enqueue("a","b","c")
// 依次出队,先进先出
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())

集合常用函数

基本操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
val list = List(1, 3, 5, 2, -4)
// 求和
println(list.sum)
// 求最大值
println(list.max)
// 求最小值
println(list.min)
// 排序
// 1.按照元素大小排序
println(list.sortBy(x => x))
// 2.按照绝对值排序
println(list.sortBy(x => x.abs))
// 3.按照元素大小升序
println(list.sortWith((x, y) => x < y))
// 简写
println(list.sortWith(_ < _))
// 4.按照元素大小降序
println(list.sortWith((x, y) => x > y))
// 5.元素反转
println(list.reverse)
// 6.自然排序
println(list.sorted)

过滤

遍历一个集合,从中获取满足指定条件的元素组成一个新的集合。

1
2
3
val list = List(1, 2, 3, 4, 5)
// 过滤操作
println(list.filter(_ > 0))

映射

将集合中的每一个元素映射到某一个函数。

1
2
3
val list = List(1, 2, 3, 4, 5)
// 映射操作
println(list.map((x) => x + 1))

扁平化

flatMap相当于先进行Map操作,在进行flat操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 扁平化
val list1 = List(List(1, 2), List(3, 4), List(5, 6))
println(list1.flatten)
// flatMap操作,相当于先进行map,再进行flatten操作。
val list2 = List("Hello Scala", "Hello World", "Hello Java")
// 使用flatMap
println(list2.flatMap(_.split(" ")))
// 先map
//println(list2.map(_.split(" "))
val list3 = list2.map((x: String) => {
x.split(" ")
})
println(list3)
// 再flatten
println(list3.flatten)

分组

按照指定的规则对集合元素进行分组。

1
2
3
val list = List(1, 2, 3, 4, 5)
// 分组操作
println(list.groupBy(_ % 2 == 0))

归约

通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。

1
2
3
4
5
6
val list = List(1, 3, 5, 2, -4)
// reduce操作
println(list.reduce(_ + _))
// 利用reduce求最小值
val min = list.reduce((x, y) => if (x > y) y else x)
println(min)

折叠

化简的一种特殊情况。

1
2
3
4
5
6
7
8
val list = List(1, 2, 3, 4, 5)
// reduce操作
println(list.reduce(_ + _))
// 利用reduce求最小值
val min = list.reduce((x, y) => if (x > y) y else x)
println(min)
// fold操作,需要指定初始值
println(list.fold(10)(_ + _)) //相当于10+1+2+3+4+5

WordCount

1
2
3
4
5
6
7
8
9
10
11
12
13
val strList = List("Hello World", "Hello Scala", "Hello Java", "Hello Scala")
// flatMap操作
val wordList = strList.flatMap(_.split(" "))
println(wordList)
// 根据单词进行分组。注意:这里不能写成groupBy(_)
val groupMap = wordList.groupBy(word => word)
println(groupMap)
// 对groupMap进行统计:对分组之后的list取长度就是出现的次数。
val countMap = groupMap.map(kv => (kv._1, kv._2.size))
println(countMap)
// 将Map转换为list并排序。根据出现的次数排序
val res = countMap.toList.sortWith(_._2 > _._2)
println(res)

模式匹配

基本格式

Scala 中的模式匹配类似于 Java 中的 switch 语法。
模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明。当需要匹配时,会从第一个case分支开始,匹配成功,执行代码块,匹配不成功,继续执行下一个case分支进行判断,如果所有case都不匹配,会执行case_分支,类似于Java中的default语句。

1
2
3
4
5
6
7
8
9
10
11
12
val a = 10
val b = 20
val c = '+'
var result = c match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "error"
}
// 输出结果:30
println(result)

模式守卫

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。

1
2
3
4
5
6
7
8
9
  val x = 120
var result = x match {
case i: Int if (i < 100 && i >= 60) => "老年"
case j: Int if (j < 60 && j >= 30) => "中年"
case k: Int if (k < 30 && k >= 20) => "青年"
case m: Int if (m < 20 && m >= 0) => "少年"
case _ => "error"
}
println(result)

模式匹配类型

匹配常量

1
2
3
4
5
6
7
8
9
10
11
12
val a = 10
val b = 20

def test(c: Char) = c match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "error"
}

println(test('+'))

匹配类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def test(x: Any) = x match {
case i: Int => "Int类型:" + i
case j: String => "String类型:" + j
case k: Boolean => "Boolean类型:" + k
case l: List[String] => "list类型:" + l.mkString(",")
case m: Array[Int] => "Array类型:" + m.mkString(",")
case _ => "其他类型"
}

println(test(1))
// 注意:这里list集合都会正常输出。List里面的类型被忽略。这就是泛型擦除。
println(test(List("a", "b")))
println(test(List(1, 2)))
// Array类型没有泛型擦除
println(test(Array(1, 2)))
// 这里会被判定为其他类型。
println(test(Array("2", "3")))
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2015-2023 henrrywan

请我喝杯咖啡吧~

支付宝
微信