写在前边的话
该笔记主要是自己回顾scala相关知识点的时候做的笔记,主要作为知识脉络整理,方便作为手册进行查看,没有源码内容,请君按需所取。
一、变量和数据类型
1、注释
单行注释: //
多行注释: /* .. */
文档注释: /**...*/
2、标识符的命名规范
scala的标识符命名的时候必须是字母、数字、下划线、$、一些特殊符号,不能以数字开头
工作中对标识符命名的时候,特殊符号不推荐使用,是scala内部使用的
工作中对变量命名的时候还是采用驼峰原则
3、变量
1、定义语法: val/var 变量名:类型 = 值
2、val与var的区别:
val修饰的变量类似java final修饰的,不能修改值
var修饰的变量类似java 非final修饰的,可以修改值
3、scala在定义变量的时候,变量类型可以省略,scala会自动推断变量类型
4、scala中如果一行只有一个语句,分号可以省略
5、scala定义变量的时候必须初始化
4、字符串
1、双引号包裹: val name = "lisi"
2、通过new的方式创建: val name = new String("lisi")
3、拼接
1、通过+拼接: val name = "aa" + "bb"
2、插值表达式: s"${变量名/值/表达式}"
4、一些方法: subString,toString,format..
"http://%s:%d".format(参数1,参数2)
%s: 代表字符串的占位符
%d: 代表整数的占位符
%f: 浮点型的占位符
5、三引号: """ .... """ [三引号一般用于sql语句]
5、键盘输入
1、读取控制台数据: StdIn.readLine/readInt/readBoolean/..
2、读取本地文件数据: Source.fromFile(path,encoding).getLines
6、数据类型
Any: 所有类的父类
AnyVal: 值类型
Byte、Short、Char、Int、Long、Float、Double、Boolean
Unit: 相当于java的void, 有一个实例()
StringOps: 对java字符串的扩展
AnyRef: 引用类型
String、集合、数组、class..
Null: 所有引用类型的子类,有一个实例null,一般用于给引用类型赋予初始值,此时必须定义变量类型
Nothing: 所有类型的子类
7、数字与字符串的转换
1、数字与数字的转换
1、低精度转高精度[Int->Long]: 自动转换
2、高精度转低精度[Long->Int]: toXXX方法
2、数字与字符串的转换
1、数字转字符串: toString/拼接
2、字符串转数字: toXXX方法
二、运算符
1、算数运算符: + - * / %
2、关系运算符: < > <= >= == !=
3、逻辑运算符: && || !
4、赋值运算符: += -= *= /=
5、位运算符: << >> >>> & | ^
scala的运算符中没有++、--、三元运算符
scala中的运算符是一个个的方法,使用和java一样
scala中方法调用的两种方式:
1、对象.方法名(参数值,..)
2、对象 方法名 (参数值,..) [如果只有一个参数,()可以省略]
三、流程控制
1、块表达式:
由{}包裹的一块代码称之为块表达式,块表达式有返回值,返回值是{}中最后一个表达式的结果值
后续基本上所有的{}包裹的代码都可以看做块表达式, for循环、while、do-while循环{}除外
2、分支控制[if语句]
1、单分支: if(布尔表达式){..}
2、双分支: if(布尔表达式){..} else {..}
3、多分支: if(布尔表达式){..} else if(布尔表达式){..} .. else {..}
scala中分支控制使用与java是一样
scala中if语句有返回值,返回值就是符合条件的分支的块表达式的结果值
3、for循环
for两个重要方法:
to方法
语法: start to end / start.to(end)
to方法生成的集合是左右闭合的集合[包含start也包含end]
until方法
语法: start until end / start.until(end)
until方法生成的集合是左闭右开的集合[包含start不包含end]
1、基本语法: for(变量名 <- 集合/数组/表达式) {循环体}
2、守卫: for(变量名 <- 集合/数组/表达式 if(布尔表达式)) {循环体} 【每次循环的时候只有if条件满足的时候才会执行循环体】
3、步长: for(变量名<- start to/until end by step) {循环体}
4、嵌套循环: for(变量名 <- 集合/数组/表达式;变量名 <- 集合/数组/表达式;..) {循环体}
5、循环之间嵌入变量: for(变量名 <- 集合/数组/表达式;变量名=值;变量名 <- 集合/数组/表达式;..) {循环体} 【嵌入变量之后,该变量可以在后续循环中使用】
6、for默认没有返回值,如果想要让for循环有返回值,可以使用yield表达式: for(变量名 <- 集合/数组/表达式) yield{循环体}
4、while、do-while循环
scala中while与do-while循环与java的while与do-while循环完全一样
5、break、continue
tips:scala中没有break与continue关键字,本质就是try()…catch{}
-
scala实现break功能:
1、导入包: import scala.util.control.Breaks._ 2、通过breakable包裹循环,通过break方法跳出循环 breakable({ while(布尔表达式){ .. if(..) break() ... } })
-
scala实现continue功能:
1、导入包: import scala.util.control.Breaks._ 2、在循环中通过breakable包裹循环体代码,通过break方法结束本次循环 while(布尔表达式){ breakable({ .. if(..) break() .. }) }
四、函数式编程
1、方法
1、定义语法:
def 方法名(参数名:类型,...):返回值类型 = {方法体}
2、方法的简化
1、如果要使用方法体块表达式的结果值作为方法的返回值,此时返回值类型可以省略
注意: 如果在方法体中有return关键字的时候不能省略返回值类型
2、如果方法体只有一个语句,此时方法体的{}可以省略
3、如果方法不需要返回值,此时=可以省略
4、如果方法不需要参数,此时定义方法的是参数列表的()可以省略
注意:
1、如果定义方法的时候参数列表的()省略了,调用的时候也不能带上()
2、如果定义方法的时候参数列表的()没有省略,此时调用的时候()可有可无
3、方法的参数
1、默认值参数[定义方法的时候可以给参数指定默认值,有默认值的参数后续在调用的时候可以不用传值]
语法: def 方法名(参数名:类型 = 默认值,...):返回值类型 = {方法体}
默认值参数一般放在参数列表最后面
2、带名参数[在方法调用的时候将值指定传递给哪个参数]
def add(x:Int=10,y:Int) = x+y
add(y=20) //带名参数的调用
3、可变参数
语法: def 方法名(参数名:类型,...,参数名:类型*):返回值类型 = {方法体}
可变参数不能与默认值参数一起使用
scala中可变参数不能直接传递数组、集合,如果想要将数组/集合的所有元素传递给可变参数可以通过 数组名:_* 的方式传递
2、函数
1、定义
1、定义语法: val 函数名 = (参数名:类型,...) => {函数体}
2、函数就是对象,函数名就是对象的引用
3、调用函数的时候,参数列表的小括号不能省略
4、函数的简化: 如果函数体中只有一行语句,此时函数体的{}可以省略
5、函数是对象,函数的类型: (参数类型,...) => 返回值类型
2、高阶函数
1、定义: 以函数作为参数/返回值的函数/方法称之为高级函数
2、高阶函数的简化[是指高阶函数调用的时候简化]
def add(x:Int,y:Int,func: (Int,Int)=>Int) = func(x,y)
val func = (x:Int,y:Int) => x+y
add(10,20,func)
1、直接传递函数值: add(10,20,(x:Int,y:Int) => x+y)
2、函数的参数类型可以省略: add(10,20,(x,y) => x+y)
3、如果函数的参数在函数体中只使用了一次,可以用_代替: add(10,20,_+_)
以下三种情况不能用_代替:
1、函数的参数定义顺序与使用的顺序不一致的时候,不能用_简化
add(10,20,(x,y)=>y-x) 不能简化为 add(10,20,_-_) [因为第N个_代表第N个参数]
2、函数体中有(),函数的参数在函数体的()中以表达式的形式存在的时候不能简化
add(10,20,(x,y)=>(x+1)*y) 不能简化为 add(10,20,(_+1)*_)
3、函数只有一个参数的时候,在函数体中没有做任何操作直接返回函数的参数,不能用_简化
def m1(x:Int,func: Int => Int) = func(x)
m1(10,x=>x) 不能简化为: m1(10,_)
4、如果函数只有一个参数,参数列表的()可以省略
m1(10,(x)=>x) 简化为 m1(10,x=>x)
3、方法与函数的区别
1、方法定义在class/object里面的时候可以重载,函数是对象,函数名就是属性名,不可以重载
2、方法定义在class/object里面的时候可以存储在方法区中,函数是对象存在堆中
4、方法与函数的联系
1、方法如果定义方法里面就是函数,不能重载
2、方法手动转成函数: 方法名 _
5、匿名函数
1、定义: 没有函数名的函数称之为匿名函数
2、匿名函数一般适用于高阶函数传参
(x:Int)=> x * x
6、函数柯里化与闭包
1、柯里化
1、定义: 有多个参数列表的方法称之为柯里化
2、语法: def 方法名(参数名:类型,..)(参数名:类型,..).. = {...}
3、函数是对象,所以函数可以作为方法的返回值
def add(x:Int) = {
val f1 = (y:Int) => x+y
f1
}
add(10)(20)
2、闭包
1、定义: 函数体/方法体中使用了不属于自身的变量的方法/函数称之为闭包
val a = 10
val func = (b:Int) => a+b
7、递归
1、定义: 自己调用自己称之为递归
递归方法/函数有两个前提条件:
1、必须要有退出条件
2、必须的定义返回值类型
2、语法:
1、递归方法:
def m1(n:Int):Int = {
if(n==1) 1 //退出条件
else n * m1(n-1)
}
2、递归函数:
val func: Int=>Int = (n:Int) => {
if(n==1) 1
else n * func(n-1)
}
8、控制抽象
1、语法: =>返回值类型
2、控制抽象其实就是一个块表达式,控制抽象只能作为方法的参数类型存在,不能单独定义。
控制抽象作为方法的参数存在的时候,代表后续调用方法的时候需要给控制抽象类型的参数传入一个块表达式,后续控制抽象在方法体中可以当做函数调用[在调用的时候不能带上()]
def m1(x:Int,func: =>Unit) = {
func //控制抽象调用
func //控制抽象调用
}
m1(10,{
.....
})
此时m1传入的块表达式不会立即执行,而是等到控制抽象调用的时候才会执行,调用一次执行一次
9、惰性求值
1、语法:
lazy val 变量名 = 值
2、变量名通过lazy修饰之后不会立即初始化,而是等到变量真正使用的时候才会初始化
五、面向对象
I、包
1、导入包
scala可以在任何地方导入包
1、导入包下所有类: import 包名._
2、导入包下某个类: import 包名.类名
3、导入包下某个类,并起别名: import 包名.{类名=>别名}
4、导入包下多个类: import 包名.{类名,类名,..}
5、导入包下除开某个类的所有类: import 包名.{类名=>_, _}
导入包的时候注意作用域问题: 在当前作用域导入的包只能在当前作用域以及子作用域使用
2、声明包
声明包必须在源文件第一行
语法: package 包名
3、创建包
语法: package 包名{....}
此种方式创建的包只能在classes目录下才能看到
4、包对象
语法: package object 包名{....}
包对象中定义的非private修饰的属性和方法可以在包下任何地方直接使用
5、包与访问修饰符结合
语法: private[包名] val 变量名:类型 = 值
private[包名]代表的是当前修饰的属性和方法/函数只能在指定包下使用
II、类和对象
1、类
1、语法: class 类名{..}
2、创建对象语法: new 类名(..)
3、定义属性和方法
1、定义属性
1、语法: [访问修饰符] val/var 属性名:类型 = 值
2、var修饰的属性可以使用_赋予初始值[使用_赋予初始值的时候,属性的类型必须定义] var name:String = _
3、val修饰的属性不可修改值,var修饰的属性可以修改值
2、定义方法:
语法: [访问修饰符] def 方法名(参数名:类型,..):返回值类型 = {...}
4、构造器
scala的构造器分为两大类: 主构造器、辅助构造器
主构造器
1、定义位置: 定义在class类名后面通过()表示
2、语法: class 类名([访问修饰符] [val/var] 属性名:类型 [= 默认值],...){...}
主构造器中val/var修饰的属性与不用val/var修饰的属性的区别:
val/var修饰的非private的属性可以在class内部和外部使用
不用val/var修饰的属性只能在class内部使用
辅助构造器
1、定义位置: 定义class内部
2、语法:
def this(参数名:类型,...){
//辅助构造器第一行必须调用其他的辅助构造器或者是主构造器
this(..)
}
2、封装 ********
java很多api底层都需要java的set/get方法,scala为了兼容java提供了一个注解@BeanProperty,
该注解能够自动生成java的set/get方法
tips:@BeanProperty注解不能用于private修饰的属性
3、继承
1、语法: class 子类 extends 父类
2、final修饰的class不能被继承
3、父类中private修饰的成员不能被继承
4、子类可以通过override关键字重写父类的方法/val修饰的属性
5、var修饰的属性不能被重写
6、子类可以通过super关键字调用父类的方法
4、多态
scala中属性与方法都是多态的
5、抽象类
1、语法: abstract 类名{...}
2、抽象类中既可以定义抽象方法也可以定义具体方法
既可以定义抽象属性也可以定义具体属性
3、抽象方法: 没有方法体的方法称之为抽象方法【定义抽象方法的时候如果方法没有定义返回值类型,默认是Unit类型】
4、抽象属性: 没有初始化的属性称之为抽象属性
5、匿名子类:
new 抽象类类名{
//重写抽象属性与方法
}
6、单例对象
1、创建语法: object object名称{..}
2、获取单例的对象: object名称
3、object中定义的所有的属性和方法都是类似java static修饰的,可以通过 object名称.方法/属性 调用
4、class中定义的所有的属性和方法都是类似java 非static修饰的
7、伴生类[class]和伴生对象[object]
前提:
1、class与object同名
2、class与object在同一个源文件中
class Person{}
object Person{}
特性: 伴生类和伴生对象可以互相访问对象private修饰的成员
apply方法:
1、定义: 必须定义在伴生对象中
2、作用: 为了简化伴生类对象的创建
有了apply方法之后,如何创建伴生类对象: object名称.apply(..) /object名称(..)
8、特质
scala是单继承,多实现
1、语法: trait 特质名{...}
2、抽象类中既可以定义抽象方法也可以定义具体方法
既可以定义抽象属性也可以定义具体属性
3、子类如何实现特质
1、子类需要继承父class: 此时extends关键字用于继承父class,特质的实现通过with关键字
2、子类不需要继承父class: 此时extends关键字用于实现第一个特质,其他特质的实现使用with关键字
4、对象混入:
1、定义:让某个对象拥有指定特质的属性/方法
2、语法: new 类名(..) with 特质名
5、特质的叠加
scala可以实现多个特质,如果这多个特质中都有一个同名方法,参数列表也一样,此时子类继承之后调用的时候默认报错,可以通过在子类中重写同名方法解决该问题。
如果在子类重写的同名方法中通过super关键字调用的时候,默认调用的时候最后一个特质的同名方法
如果想要调用指定父特质的同名方法,可以通过 super[父特质名].同名方法 的形式调用。
如果这多个父特质都继承同一个特质,此时又有通过super调用同名方法,此时调用顺序是按照继承顺序从右向左开始调用
6、自身类型
1、语法: this:类型 => [是定义在特质里面的]
2、作用: 提醒子类在继承triate的时候必须先继承/实现指定类型
9、扩展
1、类型检查和判断
1、判断对象是否属于某个类型: 对象.isInstanceOf[类型]
2、将对象强转为某个类型: 对象.asInstanceOf[类型]
3、获取对象的class形式: 对象.getClass()
4、获取类的class形式: classOf[类名]
2、type定义新类型: 给指定类型起别名
语法: type 别名 = 类型
六、集合
scala中集合分为两大类: 可变集合、不可变集合
- string属于不可变集合
- 可变数组在 scala.collection.mutable 包中
- 不可变数组在 scala.collection.immutable 包中
可变集合: 集合长度可变[可以添加/删除数据]
可变数组:
1、创建方式:
1、通过new的方式创建: new ArrayBuffer[元素类型]()
2、通过apply方法: ArrayBuffer[元素类型](初始元素,....)
2、可变数组转不可变数组: toArray
可变List:
1、创建方式:
1、通过new的方式创建: new ListBuffer[元素类型]()
2、通过apply方法: ListBuffer[元素类型](初始元素,....)
可变Set:
Set里面的元素是不重复、无序的
1、创建方式:
通过apply方法: mutable.Set[元素类型](初始元素,....)
Set不能根据角标获取元素以及修改元素
可变Map:
1、创建方式:
通过apply方法: mutable.Map[K的类型,V的类型]( K->V, (K,V),... )
2、添加元素:
put方法: put(k,v)
3、删除元素:
remove方法: remove(key)
4、获取元素: getOrElse(key,默认值) 【如果key在map中不存在则返回默认值,如果key存在则返回key的value值】
Option类型: 提醒外部当前结果有可能为空需要进行处理
Option有两个子类:
Some: 代表不为空[结果数据封装在Some对象中]
None: 代表为空
可变队列
1、创建方式: mutable.Queue[元素类型](初始元素,...)
2、添加数据: enqueue
3、删除数据: dequeue
不可变集合: 集合长度不可变[不能添加/删除数据]
不可变数组:
1、创建方式:
1、通过new的方式创建: new Array[元素类型](集合长度)
2、通过apply方法: Array[元素类型](初始元素,....)
2、不可变数组转可变数组: toBuffer
3、创建多维数组: Array.ofDim(行数,列数)
不可变List:
1、创建方式:
1、通过 :: 方法创建 : 初始元素 :: 初始元素 .... :: Nil/不可变List
Nil其实就是一个空的不可变List,Nil与不可变List的关系类似Null与String的关系
所以,Nil一般用于给不可变List赋予初始值,在赋予初始值的时候变量类型必须定义
2、通过apply方法: List[元素类型](初始元素,....)
2、添加元素
:: 用于添加单个元素
::: 用于添加一个不可变List里面的所有元素
不可变Set:
Set里面的元素是不重复、无序的
1、创建方式:
通过apply方法: Set[元素类型](初始元素,....)
Set不能根据角标获取元素以及修改元素
不可变Map:
1、创建方式:
通过apply方法: Map[K的类型,V的类型]( K->V, (K,V),... )
2、获取元素: getOrElse(key,默认值) 【如果key在map中不存在则返回默认值,如果key存在则返回key的value值】
不可变队列
队列特性: 先进先出,有序的
1、创建方式: immutable.Queue[元素类型](初始元素,...)
2、添加数据: enqueue
3、删除数据: dequeue
集合通过的添加/删除元素的方法[+、+:、:+、+=、+=:、++、++:、++=、++=:、-、–、-=、–=]
1、带+与带-的区别:
带+是添加元素,带-是删除元素
2、带=与不带=的区别
带=是修改集合本身 【带=的方法只有可变集合才有】
不带=是生成新集合,原集合没有改变
3、冒号在前、冒号在后以及不带冒号的区别
冒号在前是将元素添加在集合最末尾
冒号在后是将元素添加在集合最前面
不带冒号试将元素添加在集合最末尾
4、两个+/-与一个+/-的区别
两个+/-是添加/删除一个集合所有元素
一个+/-是添加/删除单个元素
5、update与updated的区别
update是修改集合本身
updated是生成新集合,原集合没有改变
元组:
1、定义语法
1、所有元组都通用的创建方式: (初始元素,初始元素,..)
2、只适用于二元元组的创建方式: K -> V
2、元组中最多只能存放22个元素
3、元组一旦定义元素以及长度都可以改变
4、元组的取值: 元组名._角标 [元组的角标从1开始的]
集合常用函数
1、基本属性
1、判断集合是否为空: isEmpty
2、获取集合长度: length/size
3、判断是否包含某个元素: contains(元素)
4、将集合所有元素转换成字符串: mkString(分隔符)
5、集合转成迭代器: 集合名.iterator
2、衍生集合
1、去重: distinct
2、获取集合第一个元素: head
3、获取集合最后一个元素: last
4、获取除开第一个元素的其他所有元素: tail
5、获取除开最后一个元素的其他所有元素: init
6、获取除开前N个元素的其他所有元素: drop(N)
7、获取除开最后N个元素的其他所有元素: dropRight(N)
8、获取前N个元素: take(N)
9、获取后N个元素: takeRight(N)
10、获取指定角标范围的子集合: slice(from,until) [子集合不包含Until角标的元素]
11、滑窗: sliding(size,step)
size: 窗口的大小
step: 滑动的长度
12、交集[两个集合共同的元素]: list1.intersect(list2)
13、差集[A差B的结果就是A中有B中没有的元素]: list1.diff(list2)
14、并集[合并两个集合]: list1.union(list2)
15、拉链: zip
16、反拉链[要求集合中元素二元元组]: unzip
17、将元素与角标拉链:zipWithIndex
18、反转: reverse
3、集合初级计算函数
1、获取最大值: max
2、获取最小值: min
3、求和: sum
4、根据指定字段获取最大值: maxBy(func: 集合元素类型 => B)
maxBy是根据函数的返回值取最大值
maxBy里面的函数是针对集合每个元素进行操作
5、根据指定字段获取最小值: minBy(func: 集合元素类型 => B)
minBy是根据函数的返回值取最小值
minBy里面的函数是针对集合每个元素进行操作
6、排序
1、sorted: 根据元素本身排序 【默认升序】
如果集合元素是数字则直接按照数字排序
如果集合元素是字符串则按照字典序排序
如果集合是元组则首先按照元组的第一个元素排序,如果第一个元素相同则按照第二个元素排序
2、sortBy(func: 集合元素类型 => B ): 根据指定字段排序【默认升序】
sortBy里面的函数针对集合每个元素进行操作
sortBy排序是根据函数的返回值进行排序
3、sortWith(lt: (集合元素类型,集合元素类型) => Boolean): 指定排序规则
升序: 第一个参数<第二个参数
降序: 第一个参数>第二个参数
4、集合高级计算函数
1、map(func: 集合元素类型 => B ): 映射【一对一的映射】
map里面的函数是针对集合每个元素进行操作,操作完成之后会返回一个结果 【一个元素对应一个结果】
map有返回值,一个集合通过map计算之后会生成一个新的集合: val A = B.map(..) ,此时A集合的元素个数=B集合元素个数
map的应用场景: 一对一,一般用于数据类型的转换或者是值的转换
val list2 = List(1,2,3,4,5).map(x=>x*x)
list2结果: List(1,4,9,16,25)
2、flatten: 压平
flatten是压平第二层集合: List[List[List[Int]]].flatten之后的数据类型 = List[List[Int]]
flatten只能用于集合嵌套集合的数据类型
flattne的应用场景: 一对多
3、flatMap(func: 集合元素类型 => 集合) = map + flatten
flatMap里面的函数针对集合每个元素进行操作
flatMap的应用场景: 一对多
flatMap与flatten的区别:
flatMap是先对数据进行转换之后再压平
flatten是单纯的压平,没有数据的转换操作
4、groupBy(func: 集合元素类型=>K):按照指定字段分组
groupBy里面的函数针对集合每个元素进行操作
groupBy是按照函数的返回值进行分组
groupBy的结果是Map[K,V]
map中的K是函数的返回值
map中的V是K对应原集合中的所有元素
groupBy的应用场景: 多对一
5、foreach(func: 集合元素类型 => B):Unit : 遍历元素执行指定操作
foreach与map的唯一区别:
map计算完成之后会生成一个新的集合
foreach计算完成之后没有返回值
foreach类似普通for循环,map类似yield的for循环
6、filter(func: 集合元素类型 => Boolean ): 根据指定条件过滤
filter里面的函数是针对集合每个元素操作
filter保留的是函数返回值为true的数据
7、reduce(func: (集合元素类型,集合元素类型)=>集合元素类型): 按照指定逻辑从左向右聚合
reduce里面的函数是针对每两两个元素计算
reduce里面的函数在第一次计算的时候,第一个参数的值 = 集合第一个元素
reduce里面的函数在第N次计算的时候,第一个参数的值 = N-1次的计算结果
8、reduceRight(func: (集合元素类型,集合元素类型)=>集合元素类型): 按照指定逻辑从右向左聚合
reduceRight里面的函数是针对每两两个元素计算
reduceRight里面的函数在第一次计算的时候,第二个参数的值 = 集合最后一个元素
reduceRight里面的函数在第N次计算的时候,第二个参数的值 = N-1次的计算结果
9、fold(默认值)(func: (集合元素类型,集合元素类型)=>集合元素类型): 按照指定逻辑从左向右聚合
fold与reduce类似,与reduce唯一的区别在于: fold里面的函数在第一次计算的时候,第一个参数的值 = 默认值
10、foldRight(默认值)(func: (集合元素类型,集合元素类型)=>集合元素类型): 按照指定逻辑从右向左聚合
foldRight与reduceRight类似,与reduceRight的唯一区别在于: foldRight里面的函数在第一次计算的时候,第二个参数的值 = 默认值
5、并行集合
普通List、Array等集合都是单线程操作,如果想要多线程操作集合元素需要将普通集合转成并行集合:
集合名.par
七、模式匹配
1、定义语法:
变量名 match {
case 条件 => {...} //{}可以省略
case 条件 => ...
...
case x => ... //类似switch的default,可以匹配其他所有情况
//如果x在=>右边不使用可以用_代替
//case _ => ..
}
模式匹配有返回值,返回值就是符合条件的分支的块表达式的结果值
2、守卫
有了守卫之后,不许同时满足case条件与守卫要求才算能够匹配上
变量名 match {
case 条件 if(布尔表达式) => {...}
case 条件 if(布尔表达式) => ...
...
case x if(布尔表达式) =>
}
3、匹配值
变量名 match {
case 值1 => ..
case 值2 => ..
case 值3 => ..
....
}
模式匹配的case匹配条件中如果想要使用外部的变量的值,此时变量名必须首字母大写
val Name = "hadoop"
param match {
case Name => ... //此时Name代表hadoop字符串
case name => .. //该name就是局部变量不是代表hadoop
}
4、匹配类型
变量名 match {
case x:类型1 => ..
case x:类型2 => ..
case x:类型3 => ..
...
}
5、匹配数组
变量名 match {
case Array(x) => println(匹配数组只有一个元素)
case Array(x,y,z) => println(匹配数组只有三个元素)
case Array(x,_*) => println(匹配数组至少有一个元素)
}
6、匹配List
第一种匹配方式:
变量名 match {
case List(x) => println(匹配数组只有一个元素)
case List(x,y,z) => println(匹配数组只有三个元素)
case List(x,_*) => println(匹配数组至少有一个元素)
}
第二种匹配方式:
变量名 match {
case x :: Nil => println(匹配数组只有一个元素)
case x :: y :: z :: Nil => println(匹配数组只有三个元素)
case x :: tail => println(匹配数组至少有一个元素) [tail与x一样都是一个局部变量,代表的是除开前面元素的其他所有元素集合]
}
7、匹配元组
val t = ("xx",20,"senzhen")
t match {
case (x,y,z) => ...
}
变量是几元元组,匹配条件中就只能是几元元组
8、匹配对象和样例类
样例类: 其实就是伴生对象和伴生类的封装
语法: case class 类名([val/var] 属性名:类型,...)
属性名如果不用val/var修饰,默认就是val修饰的
如何通过样例类创建对象: 样例类的类名(值,..)
case class Person(val name:String,var age:Int)
val person = Person("zhangsan",20)
样例类可以直接用于模式匹配.
persom match {
case Person(name,age) => ...
}
普通类不能直接用于模式匹配,如果想要让普通类用于模式匹配需要在伴生对象中定义unapply方法[unapply方法就是将对象解构成属性]
class Student(val name:String,var age:Int)
object Student{
def apply(String name,Int age) = new Student(name,age)
def unapply(arg:Student):Option[(String,Int)] = {
if(arg==null) None
else Some((arg.name,arg.age))
}
}
val student = Student("lisi",30)
student match{
case Student(name,age) => ..
}
9、变量声明与for循环模式匹配
val (name,age) = ("lisi",20)
val List(x,_*) = List(1,5,3,7)
val map = Map("xx"->10,"yy"->20)
for( (key,value)<- map ){....}
10、偏函数
1、定义: 没有match关键字的模式匹配称之为偏函数
2、语法:
val func: PartialFunction[IN,OUT] = {
case 条件 => ...
....
}
IN: 代表模式匹配参数类型
OUT: 代表模式匹配返回值类型
偏函数也是一个函数,所以工作中一般会将偏函数结合map、flatMap、groupBy等一起使用
val list = List( ("xx",10),("yy",20))
list.map{
case (name,age) => (name,age * age)
}
八、异常
scala中对于异常的处理方式有两种:
1、捕获异常
1、try{..}catch{..}finaly{...} [此种方式一般在工作中只用于获取外部链接[mysql链接]的时候]
2、Try(代码块).getOrElse(默认值) 【如果代码块执行成功,则返回代码块执行结果,如果代码执行失败则返回默认值】
2、抛出异常 【工作不用】
scala在方法体中可以通过throw关键字抛出异常,不需要在方法名后面通过throws声明异常
九、隐式转换
1、隐式转换方法: 将一个类型转成另一个类型
语法: implicit def 方法名(参数名:待转换类型):目标类型 = {...}
隐式转换方法的调用时机:
1、当前类型与目标类型不一致的时候: val a:Int = 10.0 [此时scala会查询是否有符合条件的隐式转换方法能够将Double转成Int]
2、当对象使用了不属于自身的属性或者方法/函数的时候:
val file = new File("..")
file.getLines() 【file不具有getLines方法,所以此时scala会查询是否有符合条件的隐式转换能够将File对象转成一个特定对象,该对象拥有getLines方法】
2、隐式参数: 在调用方法的时候给方法自动传参
语法:
1、在定义方法的时候指定后续哪个参数会自动传参: def 方法名(参数名:类型,..)(implicit 参数名:类型) = {...} [implicit标识的参数后续可以自动传参]
2、指定后续传给隐式参数的值是多少: implicit val 参数名:类型 = 值
implicit val x:Int = 10
def m1(a:Int)(implicit b:Int) = a+b
m1(20) //此时m1的b参数,scala会自动将x传递给b
3、 隐式类: 将一个类型转成隐式类的类型
语法:
implicit class 类名(属性名:待转换类型){...}
隐式类的必须要有一个特定的主构造器【该主构造器有一个参数,参数类型就是待转换类型】
后续scala会自动调用该柱构造器创建隐式类型,所以就达到了将待转换类型转成隐式类类型
隐式类不能置于最顶层,必须定义在object/class中
4、隐式转换的解析机制:
scala会首先从当前作用域以及父作用域查询是否有符合条件的隐式转换,如果有直接使用,如果没有会报错。
如果隐式参数定义在其他的object中,后续需要使用的时候需要通过 import object名称._ / import object名称.隐式转换名称 导入使用。
如果隐式参数定义在其他的class中,后续需要使用的时候需要通过 import 对象._ / import 对象.隐式转换名称 导入使用。
如果scala查询到有多个隐式转换都满足要求的时候,需要指定使用哪个隐式转换【import object名称.隐式转换名称/import 对象.隐式转换名称】
十、泛型
1、泛型方法:
def 方法名[T,U,..](参数名:T):U = {...}
2、泛型类:
class 类名[T,U,..](属性名:T,..)={
def 方法名(参数名:U) = {..}
}
泛型类中的方法/属性都是可以使用类名后面定义的泛型
3、逆变、协变、非变
逆变[-T]: 通过泛型创建对象之间颠倒了泛型的父子关系
协变[+T]:通过泛型创建对象之间继承了泛型的父子关系
非变[T]: 通过泛型创建对象之间没有任何关系
4、泛型上下限
上限[T<:类型] : 代表泛型必须是指定类型或者是子类
下限[T>:类型] : 代表泛型必须是指定类型或者是父类
5、上下文
语法: T:类型
def 方法名[T:Person](x:T) = {
val p = implicity[Person[T]]
}
等价于
def 方法名[T](x:T)(implicit p:Person[T]) = {...}