笔记整理自——厦门大学林子雨老师(Spark编程基础)
控制结构
语法
val x= 3
if (x>0){
println("正数")
}else {
println("非正数")
}
//if表达式可以有返回值
val x = 6
val a= if(x>0) 1 else -1
//while 与java相同
//略
//for循环 for(变量<-表达式) { }
for(i<- 1 to 5 by 2) println(i) //类似于迭代器 1to5类似与python 步长为2
for(i<- 1 to 5 if i%2==0) println(i)//只有i能被2整除才会执行语句 一个生成器
for(i<- 1 to 5;j<- 1 to 3) print(i*j)//两个生成器 相当于两个for循环
//for推导式: for(变量<-表达式) yield{语句块}
val r = for(i<- Array(1,2,3,4,5)if i%2==0) yield {println(i);i} //将i赋值给r: Array[Int] = Array(2,4)
异常
java : 受检异常 和 不受检异常
Scala: 所有异常都是不受检异常
try{
val f = new FileReader("input")
}catch {
case ex: FileNotFoundException =>
// 进行相关操作
//可写多个异常
}finally {
//关闭文件
}
对循环的控制
没有break 和continue
使用Breaks类 breakable break
语法
breakable{
if() break
}
package chapter01
import util.control.Breaks._//导入该类下的所有方法
object Test {
def main(args: Array[String]): Unit = {
val array =Array(1,3,10,5,4)
//写法一
breakable{
for (i<-array){
if (i>5) break //退出循环
print(i)
}
}
//写法二
for (i<-array){
breakable({
if (i>5) break //退出本次循环 相当于 continue
print(i)
})
}
}
}
数据结构
Array 数组
元素具有相同的类型
//声明一个长度为3的整型数组 每个数组元素初始化为0
val intValueArr = new Array[Int](3)
intValueArr(0) = 12 //区别圆括号
val myStrArr = Array("zs","lisi","ww") //可以不声明类型 ,Scala会自己推断类型
//多维数组的创建 使用Array的ofDim方法
val arr2 = Array.OfDim[Int](3,4) //相当于 new int[3][4]
arr2(0)(1) //使用数组
Tuple 元组
对多种不同类型的对象的一种简单封装
val tuple = ("nihao", 123)
//调用
println(tuple._1)
Collection 容器
提供了: 序列 、集合、 容器 等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IqYtk647-1593966848082)(.\图片\shell\容器层级图.png)]
Seq :按照0,1,2索引
Map:按照键索引
Set:无索引结构
序列
List 列表
是一个具体的容器类,共享相同类型的不可变的对象序列,声明时必须初始化
var strList = List("zhangsan","lisi","wangwu")
strList.head //访问头部 .tail访问尾部
//常见的构建列表的方法
val otherList = "zhaoliu" :: strList //将"zhaoliu"挂接到列表
val intList = 1::2::3::Nil // 1 2 3
Vector 向量
val vec1 = Vector(1,2)
val vec2 = 3+:4+:vec1 //加在前面
val vec3 = vec2:+5 //加在后面
vec3(3)
Range 序列
是一种特殊的带索引的不可变数字的等差序列
格式:起点 to 终点 by 步长
//一下三种写法等价
val r = new Range(1,5,1)
1 to 5
1.to(5)
//
1 to 10 by 2
0.5f to 5.9f by 0.8f
Set 集合
元素不允许重复,使用hash进行查找,查找速度很快 O(logn)
- 分为可变 和不可变 两种
切换不可变包下的 需要额外引入
//默认不可变包下的 Set
var mySet = Set("zhangsan","lisi") //不可变集合
mySet += "Scala" //这里注意 是将一个可变的值 赋给了 不可变集合,这里会生成一个新的不可变集合 重新赋值
//可变包下Set于此相似
val mySet //可与上做对比 不可变变量 赋给 可变集合
Map 映射
- 同样分为可变 和不可变 两种 默认不可变
val university = Map("XMU" -> "xiamen daxue","THU" -> "qinghua daxue")
//为了避免 键 不存在,可以先判断
val xmu = if(university.contains("XMU")) university("XMU") else 0 //注意这种写法
print(xmu)
//可变包下
university2("XMU") = "1234" //更新和添加操作相同
university2 = ("XMU" -> "xiamen daxue","THU" -> "qinghua daxue") //添加方式二
面向对象编程
类
类的定义
//修饰关键字 var 可变变量 val 不可变变量
//方法定义: def 方法名(参数列表):返回结果类型={方法体}
class Clazz{
//定义类中的字段和方法
}
- 示例
class Counter{
var value = 0
def increment(step:Int):Unit = {value += step}
def current():Int={value}
}
//实例化
val myCounter = new Counter
myCounter.value=5
muCounter.increment(3)
类的可见性:
public(默认)
protect
private
getter 与 setter
class Counter{
private var privateValue = 0
//getter 方法 注意这里是方法
def value = private Value
//setter 方法
def value = (newValue:Int){
if(newValue>0) privateValue = newValue
}
def increment(step:Int):Unit={value+=step}
def current():Int={value}
}
在调用getter方法中:myCounter.value_=(3) 可以简化为 myCounter.value_=3
交互式加载
:load path/Counter.scala
方法的定义
def 方法名(参数列表):返回结果类型={方法体}
- 注意参数不能使用var或者val修饰
- 如果没有参数可以省略括号 例如: def value = privateValue 。省不省略括号都可以不带括号调用,但是省略括号的不能带括号调用。
- 圆括号可以使用大括号代替
- 如果只有一个参数,可以省略.()调用,而使用中缀符调用 例如: a.add(b) => a add b
- 如果方法体中一条语句,大括号可以省略
构造器
类名称(参数列表)
注意:主构造器可以使用val和var关键字修饰,会成为该类的成员字段
//可以直接在类的定义中,定义字段 这是一个主构造器
class Scala_Clazz(var name:String) {
//name会成为内部字段
}
使用this(参数列表)定义辅助构造器
def this(name:String){//辅助构造器
this();//调用主构造器。一定要在主构造器中调用辅助构造器或者主构造器
this.name = name
print("***")
}
单例对象
与java中静态成员功能相同。定义在其中的所有字段、方法都是静态的,可以直接使用
使用object关键字定义单例对象
object Person{
private var lastId = 0//
def newnPersonid() = {
lastId += 1
lastId
}
}
//调用
Person.lastId
object A
class A 互为伴生关系,可以互相访问对象的成员变量和成员方法
apply方法
一条常写的语句: val myStrArr = Array(“BigData”,“Hadoop”)
Scala会调用Array这个类的伴生对象的apply方法,相当于一个工厂方法,不需要去new
def apply(param:String){
//
}
- 我们常用的一种做法
- 一个class类
- 给类定义一个伴生对象
- 类的构造方法以apply方法的形式这些在伴生对象中(一种工厂方法)
- 自动调用伴生对象的方案-》自动生成对象
示例:
class Car(name:String){
}
object Car{
def apply(name:String)=new Car(name)
}
//调用,去创造对象
val mycar = Car("BMW")
为什么采用这种方式去创建对象? 为了统一面向对象编程和函数式编程
//举个栗子
def add=(x:Int,y:Int)=>x+y
add(4,5) //函数式调用
add.appy(4,5)//对象的调用方式
函数调用 和 对象调用 可以相互转换
update方法
当对带有括号并包括一到若干参数的对象进行赋值时,编译器将调用对象的update方法,并将括号里的参数和等号右边的值作为update方法的输入参数一起执行调用
var myStrArr = new Array[String](3)
myStrArr(0) = "BigData"//实际上,调用了伴生类Array中的update方法,执行了myStrArr.update(0,"BigData")
unapply方法
用于提取apply方法的参数,相当于apply的方向解构过程
def unapply(c:Car):Option[(String,Int)]={
c.param1//c.brand
c.param2//c.price
}
//调用
Car(brand,price)
继承
抽象类
一个类中没有实现的成员,必须使用abstract修饰
成员字段必须给出类型声明
扩展类(继承)
使用extends关键字
当重载父类的抽象成员时 override关键字可选,重载父类非抽象成员时 override关键字必须使用
例如:override val carBrand = “BMW”
override def greeting(){
}
Option类
建议引用类没有返回结果时,不要使用null,而是使用Option
books.get("hadoop")//结果:Option[Book]=Some(Book(Hadoop,35.5)) 返回该键所对应的值得Some对象
books.get("hive")//结果 Option[Book]=None 不存在,返回None对象
books.get("hive").get //这样None对象得get方法会抛出异常
//推荐使用这种方式
books.get("hive").getOrElse(Book("Unknown name",0))
特质(trait)
类似与java中得接口,但是有一些区别。可以定义具体方法
trait Flyable{
var maxFlyHeight:Int //抽象字段
def fly() //抽象方法
def breathe()
{
//具体方法
print()
}
}
将特质混入类中 with extends
class Bird(flyHeight:Int) extends trait1("param1") with trait2 with trait3{
//...
}
模式匹配
使用match关键字,使用方式非常灵活
mycase match{
case 'A' => println('A case')
case 1 => println('B case')
case _ if(mymatcher%2==0) => print()
case _ => print("默认")
}
//case 后面可以跟不同类型得值
case类(样例类)
case类 = case + class ,scala会自动重载一些实用的方法,并自动生成一个伴生对象。
样例
case class Car(brand:String,price:Int)
unapply将需要返回的类型自动封装到Some对象中 被返回
包
定义,不同的包可以嵌套定义
package mypackage{}
import mypackage //import mypackage.MyClazz // import mypackage._ 全导
隐式导入(自动导入)
import java.lang._
import scala._
import Predef._
函数式编程
函数的定义
定义函数的最通用的方法时作为某个类或单例对象的成员,这种函数被称为方法。函数是一等公民
语法:def 方法名(参数列表):结果类型={方法体}
def add(x:Int,y:Int):Int={x+y}
//函数类型: (Int,Int)=>Int 只有一个参数圆括号可以省略
//值:(x,y)=>{x+y}
可以按这种方式 val num : Int =5
去定义函数 val counter : Int => Int={(value)=>value+=1}
类型 值
匿名函数(lambda表达式)
(num : Int)=>num*2
格式:(参数)=>表达式 //参数只有一个,圆括号可以省略
val myNumFunc:Int => Int = (num:Int) => num*2
print(myNumFunc(3))
//scala能够自动类型推断的,不用再声明
//例如
val myNumFunc = (num:Int) => num*2
val myNumFunc:Int => Int = (num) => num*2
"_"的作用是?
函数自变量仅在函数中出现一次时,可以使用"_"代替
//有类型时括号不能省略
val counter = (_:Int)+1 //等效于 x:int=>x+1
val add = (_:Int)+(_:Int) //等效于 (a:Int,b:Int)=>a+b
val m1 = List(1,2,3)
val m2 = m1.map(_*2) // map接受一个函数作为参数 相当于 m1.map(x=>x*2)
高阶函数
函数的参数中仍然带函数
def sum(f:Int => Int,a:Int,b:Int){
if(a>b) 0 else f(a)+sum(f,a+1,b)
}
//这里f:Int => Int是一个函数,被当作形参传入函数内
//可以选择f作为匿名函数 ,传入
容器的操作
遍历
def foreach[U] (f:Elem => U):Unit
他的函数类参数意味着你可以传入一个参数来对容器中遍历的每个元素进行操作,因此foreach也是一个高阶函数
var list = List(1,2,3)
var f = (i:Int)=>print(i)
list.foreach(f)
映射
val books = List("abc","def")
books.map(s => s.toUpperCase)//将每个字符串映射到他的大写
books.map(s => s.length)
// 一对多的映射 flatMap . falt(拍扁)
books.flatMap(s => s.toList) // books List(a,b,c,d,e,f)
过滤
val l = List(1,2,3,4,5) filter {_%2==0}
规约
reduce(f):会在每两个元素中进行一个f的运算,将返回值作为结果返回到容器中
区分左右规约顺序:reduceLeft / reduceRight
val list = List(1,2,3,4,5)
list.reduce(_+_) //将列表元素依次拿出累加 结果为 15
Fold方法可以提供初始值,同样区分左右 。
注意: Left时初始值作为第一个参数传入,Right时初始值作为第二个参数传入