Scala入门
简介
scala 是一门以 jvm 为运行环境的静态类型编程语言,具备面向对象及函数式编程的特性
六大特征
-
- Java 和 scala 可以混编
-
- 类型推测(自动推测类型)
-
- 并发和分布式( Actor )
-
- 特质,特征(类似 java 中 interfaces 和 abstract 结合)
-
- 模式匹配(类似 java 中的 switch…case )
-
- 高阶函数
Scala应用场景
- kafka :分布式消息队列,内部代码经常用来处理并发的问题,用scala可以大大简化其代码。
- spark :方便处理多线程场景,另外 spark 主要用作内存计算,经常要用来实现复杂的算法。利用scala 这种函数式编程语言可以大大简化代码。
Scala基础
标识符
- 类似Java
数据类型
- Scala的数据类型都是对象,也就是说scala没有java中的原生类型
变量和常量
-
定义变量或者常量的时候,也可以写上返回的类型,一般省略,如: val a:Int = 10
- var VariableName : DataType [= Initial Value]
- val VariableName : DataType [= Initial Value]
-
变量:在程序运行过程中其值可能发生改变的量叫做变量。
-
常量:在程序运行过程中其值不会发生变化的量叫做常量。
-
在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。
-
如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。
-
变量 :用 var 定义 ,可修改
-
常量 :用 val 定义,不可修改
运算符
-
算术运算符
-
- 加号 A + B 运算结果为 30
-
- 减号 A - B 运算结果为 -10
-
- 乘号 A * B 运算结果为 200
- / 除号 B / A 运算结果为 2
- % 取余 B % A 运算结果为 0
-
-
关系运算符
- == 等于 (A == B) 运算结果为 false
- != 不等于 (A != B) 运算结果为 true
-
大于 (A > B) 运算结果为 false
- < 小于 (A < B) 运算结果为 true
-
= 大于等于 (A >= B) 运算结果为 false
- <= 小于等于 (A <= B) 运算结果为 true
-
逻辑运算符
- && 逻辑与 (A && B) 运算结果为 false
- || 逻辑或 (A || B) 运算结果为 true
- ! 逻辑非 !(A && B) 运算结果为 true
-
位运算符
- & | ^
-
赋值运算符
逻辑语句
- Scala IF…ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。
- 同 java 一样的语法结构,单,双,多分支这样三种顺序控制结构。
循环语句
-
range
-
to
- 1 to 10 返回1到10的Range数组,包含10
-
until
- 1 until 10 返回1到10 Range数组 ,不包含10
-
step
- 步长:隔多少个数取一次,默认为1
- a to/until (b,step)
-
-
普通for循环
- for(x <- range/collection){}
- 多层for循环
-
多层for循环
- for(i <- 1 to 10; j <- 1 until 10){}
-
判断for循环
- for(i<- 1 to 10 ;if (i%2) == 0 ;if (i == 4) ){}
-
for循环与yield
- yield 关键字返回一个集合(
for {子句} yield {变量或表达式}
,原来的集合不会被
改变,只会通过你的for/yield构建出一个新的集合 - var retVal = for{ var x <- List
while循环(两种方式), while(){},do {}while()
if condition1; if condition2…
}yield x
- yield 关键字返回一个集合(
方法与函数
-
方法的定义
-
Scala 使用 def 关键字告诉编译器这是一个方法。
-
返回值类型与返回值
- 在参数后面加一个冒号和类型来显式地指定返回类型。
- 方法可以写返回值的类型也可以不写,会自动推断,但是如果有显式的Return有时候不能省略,必须写,
- Scala 中函数有返回值时,可以写 return ,也可以不写 return ,不写 return 时会把函数中最后一行当做结果返回。
- 如果去掉方法体前面的等号,那么这个方法返回类型必定是 Unit 的。scala可以把任意类型转换为 Unit 。
- 假设,函数里面的逻辑最后返回了一个 string ,那么这个返回值会被转换成 Unit ,原本逻辑的值会被丢弃。
- 如果返回值可以一行搞定,可以将 {} 省略不写
- scala 规定方法的传过来的参数为 val 的,不是 var 的。
-
-
函数的定义
-
Scala的函数是基于Function家族,0-22,一共23个Function Trait可以被使用,数字代表了Funtcion的入参个数
-
Java里只有方法都能适应一切需求,那scala又提出函数的概念肯定有意义。
- 函数可以直接赋值给变量,可以让函数很方便的传递
- 闭包(closure),可以把灵活操作代码块,从而引申出其他灵活的语法
-
-
方法与函数
-
Scala 有方法与函数,二者在语义上的区别很小
-
Scala 方法是类的一部分,而函数是一个对象 对象的引用可以赋值给一个变量。换句话来说在类中定义的函数即是方法
- Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
- Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
-
Scala 中使用 val 语句可以定义函数,def 语句定义方法
- 方法和函数的区别
-
方法和函数的区别
-
当函数定义在类中,就表示方法,其他的都称为函数
-
函数可以作为一个参数传入到方法中,而方法就不行
- 在Scala中无法直接操作方法,如果要操作方法,必须先将其转换成函数,
- 通常情况下,编译器会自动将方法转换成函数
- val f = m _ //表示将m 方法转换为函数
- 在需要函数的地方,如果传递一个方法,会自动进行ETA展开(把方法转换为函数)
-
函数必须要有参数列表,而方法可以没有参数列表
-
如果我们直接把一个方法赋值给变量会报错,(Missing arguments for methodmethod01(Int,Int))
-
-
闭包
- 闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
- 闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
字符串
- 在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类
- 在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象
- 在 Scala 中,可以使用 String Builder 类创建一个可以修改的字符串。
数组
-
创建数组的两种方式
-
数组遍历的两种方式(for–foreach)
- for(i <- arr1){
println(i)
}
arr1.foreach(i => {
println(i)
}) - //最简单写法
arr2.foreach(println)
- for(i <- arr1){
-
创建二维数组
-
创建
- val arr3 = new ArrayArray[String]
- arr3(0)=Array(“1”,“2”,“3”)
arr3(1)=Array(“4”,“5”,“6”)
arr3(2)=Array(“7”,“8”,“9”)
-
遍历
- for(i <- 0 until arr3.length){
for(j <- 0 until arr3(i).length){
print(arr3(i)(j)+" ")
}
println()
}
- for(i <- 0 until arr3.length){
-
-
可变长度数组
- val arr = ArrayBufferString
- arr.append(“hello”,“scala”)//添加多个元素
arr.+=(“end”)//在最后追加元素
arr.+=:(“start”)//在开头添加元素
arr.foreach(println)
-
数组复杂应用
-
合并数组
- var c = Array(1,2,3)
var b = Array(4,5,6)
b++c
- var c = Array(1,2,3)
-
合并数组 并将右边的类型作为最终结果返回.最后c为LinkList类型
- val a = List(1,2)
val b = scala.collection.mutable.LinkedList(3,4)
val c = a ++: b
- val a = List(1,2)
-
在数组前添加一个元素,数组在哪冒号在哪
- val k = 0
val a = List(1,2)
val c = k +: a // c中的内容是 (0,1,2)
val d = a :+ k // d中的内容是 (1,2,0)
- val k = 0
-
对数组中所有的元素进行相同的操作
- val a = List(1,2,3,4)
val c = (10 /: a)(+) // 1+2+3+4+10
val d = (10 /: a)(*) // 123410
println(“c:”+c) // c:20
println(“d:”+d) // d:240 - val a = List(1,2,3,4)
val b = new StringBuilder(“678”)
val c = a.addString(b) // c中的内容是 6781234
val d = a.addString(b,",") // 连接字符串时每个元素按分隔符隔开
val e = a.addString(b,“shou”,",",“wei”) // 在首尾各加一个字符串,并指定sep分隔符
- val a = List(1,2,3,4)
-
取出指定索引处得元素
- arr.apply(index)
-
判断两个对象是否可以进行比较
- arr.canEqual()
-
创建一个副本,不是引用,是深拷贝
- val chars = Array(‘a’,‘b’,‘c’)
val newchars = chars.clone()
- val chars = Array(‘a’,‘b’,‘c’)
-
类和对象
-
构造器
- 每个类都有一个主要的构造器,这个构造器不是单独声明的构造函数,而是和类定义交织在一起
- 除了主构造器之外,类还可以有任意多的辅助构造器(auxiliary constructor)。辅助构造器的名称为this
- 每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始
-
属性
- 类的所有属性默认都会被私有化,但是会生成getter和setter方法
- 如果属性用private修饰,生成getter和setter方法也是私有的
- 类的属性如果用val修饰,默认拥有getter方法
- 可以使用@BooleanBeanProperty注解增加Java方式的getter和setter
嵌套类
- 在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。你可以在函数中定义函数,在类中定义类。
- 在Scala中,每个实例都有它自己的Member类,就和它们有自己的members字段一样
- 要构建一个新的内部对象,你只需要简单的new这个类名:new chatter.Member。
伴生类和伴生对象
-
object由来
-
在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。
-
Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。
-
当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。
-
在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。
-
类和它的伴生对象可以互相访问其私有成员。
- 使用 object 时,不用 new ,使用 class 时要 new ,并且 new 的时候, class 中除了方法不执行,其他都执行。
-
-
scala 中的 object 是单例对象,相当于 java 中的工具类,可以看成是定义静态的方法的类。object 不可以传参数。
继承
-
Scala 使用 extends 关键字来继承一个类。Scala 只允许继承一个父类。
- 重写一个非抽象方法必须使用override修饰符。
- 只有主构造函数才可以往基类的构造函数里写参数。
- 在子类中重写超类的抽象方法时,你不需要使用override关键字。
trait
-
概述
- Scala 中 Trait (特征) 相当于 Java 的接口,实际上它比接口还功能强大。
- 与接口不同的是,它还可以定义属性和方法的实现。
- 一般情况下Scala的类可以继承多个 Trait ,从结果来看就是实现了多重继承。 Trait (特征) 定义的方式与类类似,但它使用的关键字是 trait 。
-
trait中带属性带方法实现
- 继承的多个trait中如果有同名的方法和属性,必须要在类中使用 override 重新定义。
- trait 中不可以传参数
-
trait中带方法实现
-
特征构造顺序
-
特征也可以有构造器,由字段的初始化和其他特征体中的语句构成。这些语句在任何混入该特征的对象在构造时都会被执行。
-
构造器的执行顺序
- 调用超类的构造器;
- 特征构造器在超类构造器之后、类构造器之前执行;
- 特征由左到右被构造;
- 每个特征当中,父特征先被构造;
- 如果多个特征共有一个父特征,父特征不会被重复构造
- 所有特征被构造完毕,子类被构造
-
构造器的顺序是类的线性化的反向。线性化是描述某个类型的所有超类型的一种技术规格
-
修饰符
-
概述
- Scala 访问修饰符基本和Java的一样,分别有:private,protected,public
- 如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public
- Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员
-
private
- 仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类
- 如果字段是私有的,则getter和setter方法也是私有的
- 如果字段是val,则只有getter方法被生成
- 如果你不需要任何getter或setter,可以将字段声明为 private[this]
-
protected
- 在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问
-
public
- 这样的成员在任何地方都可以被访问
-
作用域保护
-
Scala中,访问修饰符可以通过使用限定词强调
- private[x]
- protected[x]
-
这里的x指代某个所属的包、类或单例对象。
-
如果写成private[x],读作"这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。
-
键盘标准输入
-
在 scala 中只需要导入对应的包,比 java 还要简单不需要实例化对象
-
例
- println(“请输入姓名”)
val name = StdIn.readLine()
println(“请输入年龄”)
val age = StdIn.readInt()
printf(“您输入的姓名是%s,年龄是%d”,name,age)
- println(“请输入姓名”)
-
符号 含义
%d 十进制数字
%s 字符串
%c 字符
%e 指数浮点数
%f 浮点数
特殊符号的使用场景
-
(=>)
-
表示函数的返回类型(Function Type)
- 当函数只有一个参数的时候,函数类型里面括起来函数参数的括号是可以省略的。
-
-
匿名函数
- 匿名函数定义, 左边是参数 右边是函数实现体
- var function0 = () => Math.random()
var function1 = (x: Int) => Math.random()
-
case语句
- val x = 10;
val y = 20;
val max = x > y match {
case true => x
case false => y
- val x = 10;
-
By-Name Parameters(传名参数)
- 传名参数在函数调用前表达式不会被求值,而是会被包裹成一个匿名函数作为函数参数传递下去
- def doubles(x: => Int)
-
下划线(_)
-
作为通配符,类似Java中的*
-
:_*作为一个整体,告诉编译器你希望将某个参数当做参数序列处理
- //将 1 to 5 当做参数序列处理
val s = sum(1 to 5:_*)
- //将 1 to 5 当做参数序列处理
-
指代一个集合中的元素
- //在一个Array a中筛选出偶数, 并乘以2,可以用以下这个办法
a.filter(%2==0).map(2*)
//要对缓冲数组ArrayBuffer b排序,可以这样:
val bSorted = b.sorted(_)
- //在一个Array a中筛选出偶数, 并乘以2,可以用以下这个办法
-
在元组中,可以用方法1,2,_3访问组员, _
- 例如a.2 其中句点可以用空格替代
-
使用模式匹配可以用来获取元组的组员,例如
- val (first,second,third)=t
- 但如果不是所有的部件都需要,那么可以在不需要的部件位置上使用,比如上一例中val(first,second,)=t
-
下划线_代表的是某一类型的默认值.
- var name = _ (前提必须是变量var 不能是val)
对于Int来说,它的默认值为0
对于Double来说,它的默认值是0.0
对于引用类型来说,它是null.
- var name = _ (前提必须是变量var 不能是val)
-
-
复杂操作符
-
::
- 该方法被称为cons,意为构造,向队列的头部追加数据,创造新的列表
- 用法为 x::list ,其中 x 为加入到头部的元素,无论 x 是列表与否,它都只将成为新生成列表的第一个元素
- 也就是说新生成的列表长度为list的长度+1(btw, x::list 等价于 list.::(x) )
-
:+ 和 +:
- 两者的区别在于 :+ 方法用于在尾部追加元素, +: 方法用于在头部追加元素
- 和 :: 很类似,但是 :: 可以用于pattern match ,而 +: 则不行
- 关于 +: 和 :+ ,只要记住冒号永远靠近集合类型就OK了
-
:::
- 该方法只能用于连接两个List类型的集合
-
++
- 该方法用于连接两个集合, list1++list2 。
-
Scala参数传递
传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
- Call-by-Value 避免了参数的重复求值,效率相对较高
传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部
- Call-by-Name 避免了在函数调用时刻的参数求值,而将求值推延至实际调用点,但有可能造成重复的表达式求值。
Scala函数深化
指定参数名
- 一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递
- 但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数
- def main(args: Array[String]) {
printInt(b=5, a=7);
}
def printInt( a:Int, b:Int ) = {
println("Value of a : " + a );
println("Value of b : " + b );
}
可变参数
- Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表
- 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)
递归函数
- 自己调用自己
- 必须有程序的出口
默认参数值
-
Scala 可以为函数参数指定默认参数值,使用了默认参数
- 调用函数的过程中如果没有传递参数,函数就会调用它的默认参数值,
- 调用函数的过程中如果传递了参数,则传递值会取代默认值。
匿名函数
-
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。
-
- 有参匿名函数
-
- 无参匿名函数
-
- 有返回值的匿名函数
-
-
可以将匿名函数返回给val定义的值
-
匿名函数不能显式声明函数的返回类型
嵌套函数
- 可以在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。
偏应用函数
- Scala 偏应用函数是一种表达式,你不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数
- 例如:一个参数是完全相同,另一个参数不同,在这样一个情况之下,我们可以使用偏应用函数来进行优化
- val date = new Date()
log(date ,“log1”)
log(date ,“log2”)
log(date ,“log3”) - //想要调用log,以上变化的是第二个参数,可以用偏应用函数处理
val logWithDate = log(date,_:String)
logWithDate(“log1”)
logWithDate(“log2”)
logWithDate(“log3”) - 以上两个效果相同
高阶函数
-
就是操作其他函数的函数
-
可以使用其他函数作为参数,或者使用函数作为输出结果
- 函数的参数是函数
- 函数的返回是函数
- 函数的参数和函数的返回是函数
柯里化函数
-
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。
-
新的函数返回一个以原有第二个参数为参数的函数。
- fn(a,b,c,d)=>fn(a)(b)©(d);
- fn(a,b,c,d)=>fn(a,b)©(d);
- fn(a,b,c,d)=>fn(a)(b,c,d);
-
可以理解为高阶函数的简化
Scala集合深化
简介
-
集合
-
scala.collection 中的所有集合。这些都是高级抽象类或特征,通常具有可变和不可变的实现
-
scala.collection.immutable中的所有集合。不变的
- 不会被改变。但仍可以进行模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
-
scala.collection.mutable中的所有集合。可变的
- 可以在适当的地方被更新或扩展,可以修改,添加,移除一个集合的元素。
-
-
Trait Traversable
- Traversable(遍历)是容器(collection)类的最高级别特性(trait),它唯一的抽象操作是foreach:
- 实现Traversable的容器(collection)类仅仅需要定义与之相关的方法,其他所有方法可都可以从Traversable中继承
-
Trait Iterable
-
容器(collection)结构的上层还有另一个trait。这个trait里所有方法的定义都基于一个抽象方法,迭代器(iterator,会逐一的产生集合的所有元素)
-
foreach是Traversable所有操作的基础,所以它的性能表现很关键
-
Iterable有两个方法返回迭代器:grouped和sliding。这些迭代器返回的不是单个元素,而是原容器(collection)元素的全部子序列
- grouped方法返回元素的增量分块
输出指定个数数据,不重复前面块的数据 - sliding方法生成一个滑动元素的窗口
输出指定个数数据,但每次向后滑动一位
- grouped方法返回元素的增量分块
-
-
Trait Seq
-
Seq trait用于表示序列
- 序列,指的是一类具有一定长度的可迭代访问的对象,其中每个元素均带有一个从0开始计数的固定索引位置。
-
常见操作
-
索引和长度的操作 (apply、isDefinedAt、length、indices、lengthCompare)
-
索引检索操作(indexOf、lastIndexOf、indexofSlice、lastIndexOfSlice、indexWhere、lastIndexWhere、segmentLength、prefixLength)
- 返回等于给定值或满足某个谓词的元素的索引。
-
加法运算(+:,:+,padTo)
- 在序列的前面或者后面添加一个元素并作为新序列返回。
-
更新操作(updated,patch)
- 替换原序列的某些元素并作为一个新序列返回。
-
排序操作(sorted, sortWith, sortBy)
- 对序列元素进行排序。
-
反转操作(reverse, reverseIterator, reverseMap)
- 将序列中的元素以相反的顺序排列。
-
比较(startsWith, endsWith, contains, containsSlice, corresponds)
- 对两个序列进行比较,或者在序列中查找某个元素。
-
多集操作(intersect, diff, union, distinct)
- 对两个序列中的元素进行类似集合的操作,或者删除重复元素
-
-
trait Seq 具有两个subtrait LinearSeq和IndexedSeq。
- 线性序列具有高效的 head 和 tail 操作
- 索引序列具有高效的apply, length, 和 (如果可变) update操作。
- Vector 类提供一个在索引访问和线性访问之间有趣的折中。它同时具有高效的恒定时间的索引开销,和恒定时间的线性访问开销。
-
-
Trait Set
-
集合是不包含重复元素的可迭代对象
-
可变集合和不可变集合提供了+和++操作符来添加元素,-和–用来删除元素
-
目前可变集合默认使用哈希表来存储集合元素,非可变集合则根据元素个数的不同,使用不同的方式来实现
-
集合的两个特质是 SortedSet 和 BitSet.
- SortedSet 是指以特定的顺序(可以在创建集合时设置顺序)排列其元素(使用iterator或foreach)的集合,默认表示是有序二叉树,即左子树上的元素小于所有右子树上的元素
- BitSet是由单字或多字的紧凑位实现的非负整数的集合。其内部使用 Long 型数组来表示
-
-
Trait Map
- Map是一种可迭代的键值对结构
- Scala的Predef类提供了隐式转换,允许使用另一种语法:key ->Scala的Predef类提供了隐式转换,允许使用另一种语法:key ->
value 代替(key,value)
list
-
简介
-
Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同
- 列表是不可变的,值一旦被定义了就不能改变,
- 列表具有递归的结构
-
构造列表的两个基本单位是 Nil 和 ::
- 不要忘记 Nil 是 长度为0的List
-
Scala列表有三个基本操作
- head 返回列表第一个元素
- tail 返回一个列表,包含除了第一元素之外的其他元素
- isEmpty 在列表为空时返回true
-
-
创建list
-
一维
- val nums: List[Int] = List(1, 2, 3, 4)
-
二维
- val dim: List[List[Int]] =
List(
List(1, 0, 0),
List(0, 1, 0),
List(0, 0, 1)
)
- val dim: List[List[Int]] =
-
-
list遍历
- for , foreach 两种方式
-
list方法举例
-
filter :过滤元素
-
count :计算符合条件的元素个数
-
map :对元素操作
-
flatmap :压扁扁平,先map再flat
-
-
可变长度List
-
val listBuffer = ListBufferInt
- listBuffer.append(6,7,8,9) //追加元素
- listBuffer.+=(10) //在后面追加元素
- listBuffer.+=:(100) //在开头追加元素
-
Set
-
简介
-
Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。
-
Scala 集合分为可变的和不可变的集合。
-
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用scala.collection.mutable.Set 包。
-
Scala集合有三个基本操作
- head 返回集合第一个元素
- tail 返回一个集合,包含除了第一元素之外的其他元素
- isEmpty 在集合为空时返回true
-
-
创建Set
- val set2 = Set(1,2,5)
-
遍历Set
- 同样两种方式 for , foreach
-
Set方法举例
-
- 交集: intersect , &
-
- 差集: diff , &~
-
- 子集: subsetOf
-
- 最大: max
-
- 最小: min
-
- 转成数组: toList
-
- 转成字符串: mkString
-
-
可变长度Set
- val set = SetInt
- set.add(100)
- set.+=(200)
- set.+=(1,210,300)
Map
-
简介
- Map(映射)是一种可迭代的键值对(key/value)结构。
- 所有的值都可以通过键来获取。
-
创建Map
- Map(1 –>“shanghai”) 键 -> 值的对应关系创建
- Map((1,“shanghai”)) 元组的形式(key,value)
- 创建 map 时,相同的 key 被后面的相同的 key 顶替掉,只保留一个。
-
获取Map的值
- map.get(“1”).get
- map.get(100).getOrElse(“no value”) 此种方式如果 map 中没有对应项则赋值为 getOrElse里面的值。
-
遍历Entry
-
for
- for(x <- map){
println("====key:"+x._1+",value:"+x._2)
}
- for(x <- map){
-
foreach
- map.foreach(f => {
println(“key:”+ f._1+" ,value:"+f._2)
})
- map.foreach(f => {
-
-
遍历key
- val keyIterable = map.keys
keyIterable.foreach { key => {
println(“key:”+key+", value:"+map.get(key).get)
} }
- val keyIterable = map.keys
-
遍历value
- val valueIterable = map.values
valueIterable.foreach { value => {
println("value: "+ value)
} }
- val valueIterable = map.values
-
合并Map
- map1.++(map2).foreach(println)
- 注意:合并 map 会将 map 中的相同 key 的 value 替换.
-
Map方法举例
- filter :过滤,留下符合条件的记录
- count :统计符合条件的记录数
- contains : map 中是否包含某个 key
- exist :符合条件的记录存在与否
-
可变长度Map
- val map = Map[String,Any]((“name”, “lisi”),(“money”,200))
- map+=(“age”->18)//单个添加
- map+=(“age1”->18,“age2”->18,“age3”->18)//多个添加
- map-=(“age”)//删除直接针对key值。map.remove(“age”)
元组Tuple
-
定义
- 大体与列表一样,不同的是元组可以包含不同类型的元素
- 元组的值是通过将单个的值包含在圆括号中构成的
-
创建元组
- val tuple1 = new Tuple(1)
- val tuple2 = Tuple2(1,2)
- val tuple3 =(1,2,3)
- 取值用 ._X 可以获取元组中的值
- 注意:tuple最多支持22个参数
-
遍历元组
- 虽然元组不是集合,但是在遍历使用时可以当作一个集合来用
- 通过 tuple.productIterator 得到迭代器,进而实现遍历
- 迭代器只能使用一次,下次还想遍历就需要构建一个新的 iterator
- val tupleIterator = tuple22.productIterator
while(tupleIterator.hasNext){
println(tupleIterator.next())
}
-
元组的简单方法
- 例如 swap , toString 方法。注意 swap 的元素翻转只针对二元组。
Option
-
Scala Option(选项)类型用来表示一个值是可选的(有值或无值)
-
Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None
- val value1: Option[String] = myMap.get(“key1”)
-
getOrElse() 方法来获取元组中存在的元素或者使用其默认的值
- val a:Option[Int] = Some(5)
- a.getOrElse(0)
-
isEmpty() 方法来检测元组中的元素是否为 None
- val a:Option[Int] = Some(5)
- a.isEmpty
WordCount
- 统计一个数组中每个单词出现的次数
模式匹配
Scala 提供了强大的模式匹配机制,应用也非常广泛。
一个模式匹配包含了一系列备选项,每个都开始于关键字 case 。
每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
注意点
- 1.模式匹配不仅可以匹配值,还可以匹配类型
- 2.模式匹配中,如果匹配到对应的类型或值,就不再继续往下匹配
- 3.模式匹配中,都匹配不上时,会匹配到 case _ ,相当于default
偏函数
定义
-
偏函数(Partial Function),是一个数学概念它不是"函数"的一种, 它跟函数是平行的概念。
-
Scala中的Partia Function是一个Trait,其的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果
-
{
case 1=>“hello world 1”
case 2=>"hello world 2
}- 模式匹配1,或者2,则会返回一个字符串
- 但是这里不能匹配除1,2之外的其他的Int,如果你传入一个3,那么不会有任何的返回
- 如果我们把这个当做一个函数来看待,那么他就是只接受Int类型且值为1或者2的函数调用
-
-
如果一个方法中没有 match 只有 case ,这个函数可以定义成 PartialFunction偏函数
-
偏函数定义时,不能使用括号传参,默认定义 PartialFunction 中传入一个值,匹配上了对应的case ,返回一个值,只能匹配同种类型
-
一个 case 语句就可以理解为是一段匿名函数。
方法
-
isDefinedAt
- 这个函数的作用是判断传入来的参数是否在这个偏函数所处理的范围内
- def pf: PartialFunction[AnyVal, String] = {
case 1 => “One”
case 2 => “Two”
case 3 => “Three”
} - pf.isDefinedAt(1)
-
orElse
- 将多个偏函数组合起来使用,效果类似case语句
- def pf = onePf orElse twoPf orElse threePf
-
andThen
- 相当于方法的连续调用,比如g(f(x))
- var pf12 = onePf andThen twoPf
- pf12(1)
-
applyOrElse
- 它接收2个参数,第一个是调用的参数,第二个是个回调函数
- 如果第一个调用的参数匹配,返回匹配的值,否则调用回调函数
- def onePf: PartialFunction[Int, String] = {
case 1 => “one”
} - onePf.applyOrElse(1, { num: Int => “more” })
样例类(case classes)
概念
-
case class是一种可以用来快速保存数据的类,可以认为是java中的pojo类,用于对象数据的保存
-
默认实现方法
-
apply :
- 不需要使用new关键字就能创建该类对象
-
unapply :
- 可以通过模式匹配来获取类属性,是Scala中抽取器的实现和模式匹配的关键方法。
-
getter /setter :
- 默认构造参数默认被声明为val,实现了类构造参数的getter方法
- 如果构造参数是声明为var类型,实现setter和getter方法(不建议)
-
toString : equals : hashCode : copy
- JavaBean规范的常见方法
-
实现
隐式转换
概念
-
隐式转换是在 Scala 编译器进行类型匹配时,如果找不到合适的类型,那么隐式转换会让编译器在作用范围内自动推导出来合适的类型
-
隐式的使用方式
- 1.将方法或变量标记为implicit
- 2.将方法的参数列表标记为implicit
- 3.将类标记为implicit
-
Scala支持两种形式的隐式转换
- 隐式值:用于给方法提供参数
- 隐式视图:用于类型间转换或使针对某类型的方法能调用成功
- 隐式参数:参数列表
Scala隐式值
- 将p变量标记为implicit,所以编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺少参数
- 如果此时你又在REPL中定义一个隐式变量,再次调用方法时就会报错
- 隐式转换必须满足无歧义规则,在声明隐式参数的类型是最好使用特别的或自定义的数据类型,不要使用Int,String这些常用类型,避免碰巧匹配
Scala隐式视图
-
隐式转换函数是指在同一个作用域下面,一个给定输入类型并自动转换为指定返回类型的函数,这个函数和函数名字无关,和入参名字无关,只和入参类型以及返回类型有关
-
隐式转换为目标类型:把一种类型自动转换到另一种类型
-
隐式转换调用类中本不存在的方法
- 编译器在xiaoming对象调用时发现对象上并没有learn方法,此时编译器就会在作用域范围内查找能使其编译通过的隐式视图
- 找到learnView方法后,编译器通过隐式转换将对象转换成具有这个方法的对象,之后调用learn方法
Scala隐式类
-
简介
- 使用 implicit 关键字修饰的类就是隐式类。若一个 变量A 没有某些方法或者某些变量时,而这个 变量A 可以调用某些方法或者某些变量时
- 可以定义一个隐式类,隐式类中定义这些方法或者变量,隐式类中传入 A 即可。
-
注意点
- 隐式类必须定义在类,包对象,伴生对象中。
- 隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。
-
具体写法
Scala隐式参数
-
隐式参数
- 隐式参数同样是编译器在找不到函数需要某种类型的参数时的一种修复机制,我们可以采用显式的柯里化式的隐式参数申明,也可以进一步省略,采用 implicitly 方法来获取所需要的隐式变量。
- 隐式参数相对比较简单,Scala中的函数申明提供了隐式参数的语法,在函数的最后的柯里化参数
- 列表中可以添加隐式 implicit 关键字进行标记, 标记为 implicit 的参数在调用中可以省略
- Scala编译器会从当前作用域中寻找一个相同类型的隐式变量,作为调用参数。
-
注意点
- 同类型的参数的隐式值只能在作用域内出现一次,同一个作用域内不能定义多个类型一样的隐式值。
- implicit 关键字必须放在隐式参数定义的开头
- 一个方法只有一个参数是隐式转换参数时,那么可以直接定义 implicit 关键字修饰的参数,调用时直接创建类型不传入参数即可。
- 一个方法如果有多个参数,要实现部分参数的隐式转换,必须使用柯里化这种方式,隐式关键字出现在后面,只能出现一次。
-
具体写法
Actor Model
Actor的特征
- ActorModel 是消息传递模型,基本特征就是消息传递
- 消息发送是异步的,非阻塞的
- 消息一旦发送成功,不能修改
- Actor 之间传递时,接收消息的 actor 自己决定去检查消息, actor 不是一直等待,是异步非阻塞的
具体写法
-
Actor发送接收消息
-
Actor与Actor之间通信
- 很像storm数据传递