Spark基础之Scala知识总结

史上最全的Scala知识点整理

第一章 变量及基本数据类型

1.1 注释

(1)单行注释: //

(2)多行注释:/* */

(3)文档注释:/** */

(4)格式调整:ctrl+alt+L

1.2 标识符命名规范

(1)以字母或者下划线开头,后接字母、数字、下划线

(2)以操作符开头,且只包含操作符(+ - * / # !等)

(3)第一种和第二种拼接,第一种在前,二者以下划线分隔

(4)用反引号``包括的任意字符串,即使是关键字(39个)也可以

1.3 变量

(1)基本语法:val | var 变量名[:变量类型] = 变量值

(2)声明变量时可不指定类型,自动类型推断;

(3)声明变量时必须指定初始值;

(4)val修饰的变量值/对象值不可以变,var修饰的变量值/对象值可以变;

1.4 字符串

(1)双引号 “ ” | 三引号 “ ” “ “ ” “

(2)插值表达式 s"string ${变量名/表达式}"

(3)占位符 %s 字符串 %f 浮点型 %d 整型

//插值表达式
val hello = "hello"
val world = "world"
val hello_world = s"${hello} ${world}"
println(hello_world)
    
//占位符 %s 字符串占位符, %f 浮点型占位符, %d 整数占位符
val name = "%s"
println(String.format(name,"hello"))

val str = "hello %s %f %d fighting"
println(str.format("jason",2.34,25))
    
//printf 格式化输出
printf("hello %s","world")

1.5 数据读取

//键盘输入
val scan = StdIn.readLine("请输入任意值:")
println(scan)
//文件读取
val source: BufferedSource = Source.fromFile("d:/aaa.txt", "utf-8")
println(source.getLines().toBuffer)

1.6 数据类型

在这里插入图片描述

1.6.1 概述

(1)Scala是完全面向对象的语言,Any为所有类的父类;

(2)AnyVal是数值类型,包括类似java中的基本数量类型、StringOps、Unit…

AnyRef是引用类型,String、类、集合…

​ Unit类表示无返回值,是数值类型,等同于void,实例为()

(3)NUll是所有引用类型的子类,其实例为null,当使用null作为变量初始值时不可以使用类型推断!

(4)Nothing是所有类的一个抽象子类,用来抛异常使用;

(5)其它数据类型说明基本与Java中描述一致。

1.6.2 类型转换

1)数值类型间转换

(1)符合自动类型提升原则,即从低精度自动提升为高精度;

(2)Byte、Short、Char互相运算时会先转为Int类型;

(3)类型强转需调用toXxx方法,a:Int=b.toInt

//低精度能够自动转成高精度
var a:Int = 100
var b:Long = a
println(b)

//高精度的不能自动转成低精度的,要想转换需要调用toXXX方法
var c:Double = 10.0
var d:Float = c.toFloat
println(d)

2)字符串与数值类型转换

/**
*数字转字符串:数字拼接空字符串
*字符串转数字:调用toXXX方法
*/
val e = 10
val s:String = e.toString

val a = "10.0"
//val b:Int = a.toInt 报错,因为“.”识别不清代表什么
val b:Double = a.toDouble
val c:Int = a.toDouble.toInt

1.7 运算符

​ 与Java中运算符基本一致,Scala中没有 ++ – 三元运算符

第二章 流程控制

2.1 块表达式

//块表达式有返回值,返回值为{}中最后一个表达式的结果值
val block = {
println("hello ...")
println("霸王别姬")
val result = 1 + 1
result
} //运行结果:2

2.2 If判断

/*
java中if的用法:
 1、单if
 2、if-else
 3、if-else if-..-else
scala中if用法与java完全一样,唯一不同的地方在于scala的if表达式有返回值
*/
val result1 = if( 10%2==0) {
    println("hello")
    val name = "string"
    s"java ${name}"
}

2.3 For循环

2.3.1 Scala中方法调用的两种方式
//1.与java相同  对象名.方法名(形参)
1.to(10//2.可以忽略 .  对象名 方法名(形参)
1 to(101 to 10 //只有一个参数可以省略括号
2.3.2 基本语法
//for循环基本语法:  for(变量名 <-  数组/集合/表达式)

//1. to代表左闭右闭  until代表左闭右开
for(i <- 1 to 10) { println(i) }
for(i <- 1 until 10) { println(i) }

//2.指定步长
for(i <- 1 to 10 by 2) { println(i) }

//3.守卫 条件判断式 true则进入循环
for(i <- 1 to 10 if i % 2 == 0) { println(i) }

//4.引入变量  用“;”分割
for(i <- 1 to 10;j = i * i) { println(i + j) }

//5.嵌套循环  多个循环语句用“;”分割
for(i <- 1 to 10;j <- 1 until 4;k...) { println(i) }
2.3.3 循环返回值
//需要使用yield关键字
val list = for(i <- 1 to 10 if i%3=0) yield i
println(list) //Vector(3,6,9)
//可以将结果集转换为数据
println(list.toBuffer) //ArrayBuffer(3,6,9)

2.4 While循环

​ Scala的while循环、do while循环与java的完全一样

2.5 Switch

​ Scala中没有Switch case

2.6 中断循环

//scala中没有break和continue 因此中断循环需要使用抛异常的方式 通过Breaks封装类

//基本语法
import scala.util.control.Breaks._
breakable{ 循环体 条件判断语句 break()}

//breakale()指定结束循环位置,实际上为try catch
//当breakable()后的逻辑代码不止一行时需要使用{循环内容}
def breakable(op: => Unit) {
  try {
    op
  } catch {
    case ex: BreakControl =>
      if (ex ne breakException) throw ex
  }
}
//break()执行时结束循环
*/
def break(): Nothing = { throw breakException }

//案例1:类似于break的结束当前循环
import scala.util.control.Breaks._
breakable(
    for(i <- 1 to 100){
        if(i % 10 == 0) break()
    println(i)
})
//案例2:类似于continue的结束当次循环
var i = 0
while(i < 100){
  breakable({
    if(i % 2 == 0) break()
    println(i)})
  i = i + 1
}

for(i <- 1 to 100){
  breakable({
    if(i % 10 == 0) break()
      println(i)
    })
}

第三章 面向函数编程

3.1 概念

面向对象:解决问题时,将问题拆解成为一个个的小问题(对象),分别解决对象关系(封装/继承/多态)

面向函数:编程关心的是问题的具体解决步骤(封装功能):入参和出参

Scala是一门完全面向对象的语言,同时也是一门完全面向函数的语言!!!

3.2 函数的定义

3.2.1 基本语法
//*def 方法名 ( 参数名:参数类型,...):返回值类型 = {方法体}
def add(x:Int,y:Int):Int = {x+y}

//*val 函数名 = (参数名:参数类型,...) => {函数体}
val sum=(x:Int,y:Int) => {x+y}

3.2.2 方法与函数
--方法与函数的区别:
  (1) 方法如果定义在class中是可以重载的,函数不可以
  (2) 方法保存在方法区,函数保存在堆空间中
  (3) 方法是随着class的加载而加载的,函数是创建对象的时候加载的
  
--方法与函数的转换:
  def function(x:Int,y:Int):Int = x+y
  val fun = function _

3.2.3 参数与返回值
//1.多种组合类型
//有参 有返回值
def add1(x:Int,y:Int):Int = {x+y}
//无参 有返回值
def add2():String = {"hello jason"}
//有参 无返回值
def add3(x:Int,y:Int) = {println(x+y)}
//无参 无返回值
def add4() = {println("hello jason")}

/*2.可变形参  
  (1)放在形参列表的最后,因此只能有一个可变形参
  (2)可变形参实际上为一个集合,通过遍历的方式传入每一个值
  (3)可变形参中可以使用带名参数调用
  (4)Java中可变参数可以传入数组,但是scala中不可以  */
def sum(a:String,b:String*):Unit = {println(s"a:${a},b:${b}")}

def fun(a:Int*):Unit = {println(a)}
fun(7,7,7) //WrappedArray(7,7,7)
    
def foreach(array:Int*):Int={
    var sum:Int = 0
    for (elem <- array) {sum = sum + elem} sum }  //此时的array为一个集合

//scala中的可变形参无法传入数组,但是可以使用 :_* 进行转换
readline(getPath(7):_*)

//3.参数默认值
  /**
  *(1)当有多个参数时,有默认参数的要放在形参列表最后
  *(2)默认参数与可变形参不可以结合使用
  *(3)给默认参数赋值会将默认值覆盖掉
  */
def people(name:String,age:Int=18)={println(s"name:${name},age:${age}")}
people("jason") //name:jason,age:18

//4.带名参数 —— 在调用函数时,可指定参数进行赋值,此时有默认值的参数可以不在形参列表最后
def student(age:Int=1001,name:String):Unit={println(s"age:${age},name:${name}")}
student(name="jason")  //age:1001,name:jason

3.3 至简原则

3.3.1 函数定义
def desc(name:String):String={
    return s"${name} is good!"
}

//1.return: 有返回值类型时,将块中最后一个执行语句返回,因此return可省略
   //如果有return,必须指定返回值类型;如果返回值类型为Unit,则return不起作用
def desc(name:String):String={
    s"${name} is good!"
}

//2.返回值类型:  可以通过{}最后一个执行语句自动推断判断返回值类型
def desc(name:String)={
    s"${name} is good!"
}

//3.{}:  函数体只有一行时,{}可以省略
def desc(name:String)= s"${name} is good!"
    
//4.():  无参时,可以省略()
    //说明:定义时省略(),调用时必须不加();
    //     定义时无参(),调用时可加可不加()
def desc2()= "jason is good!"
desc2()/desc2

//5.=:  如果不需要返回值,=可以省略
def desc(a:String) {println(a)}

3.3.2 函数调用
def addfun(x:Int,y:Int,func:(Int,Int)=>Int) = {func(x,y)}
//调用函数
addfun(100,200,(x:Int,y:Int) => x+y )
//1.传入参数时可以省略参数类型
addfun(100,200,(x,y) => x+y )
//2.参数只调用了一次,可以使用 _ 代替
addfun(100,200,_+_)
/*补充说明:
 (1)参数只能调用一次,且必须按照形参列表中的顺序进行调用
 (2)参数只有一个,在函数体中直接返回,则不能用_  x=>x
 (3)如果函数体中嵌套了表达式,且参数在函数体的表达式中被引用,则不能使用_
   printMsg("wangwu",x=>println(x*10))  
     函数体中有(),而且x在函数体的()中构成了表达式,此时不能用_代替
   printMsg("wangwu",x=>println(x))  
     可以用_代替,代替之后变成了printMsg("wangwu",println(_))  此时单个_构不成表达式会跳出_

3.4 匿名函数

//匿名函数:没有def和函数名,将匿名函数作为参数传递给另一个函数
val 函数名=(参数名:参数类型...) => { ... }

val function =  () => println("jason")
function()

3.5 柯里化&闭包

//1.柯里化: 有多个参数列表的方法称之为柯里化 —— 函数也是对象,因此对象.对象.方法必然需要柯里化
def 函数名(参数名:参数类型...)(参数名:参数类型...)... = { ... }

def add3(x:Int,y:Int) = {
    val add2 = (a:Int,b:Int) => {
      val add1 = (z:Int) => x+y+a+b+z
      add1
    }
    add2
  }
println(add3(1,2)(3,4)(5))  //15
    
//2.闭包: 函数体中使用了不属于函数本身作用域的参数的函数称之为闭包
val y = 10
val func = (x:Int) => {
  val name ="jason"
  x+y
}

3.6 递归函数

//Scala中使用递归函数的两个条件:1.有终止条件  2.函数必须明确定义返回值
def sum(n:Int):Int={
    if(n==1){
        1
    }else{
        n+sum(n-1) 
    }       
}

3.7 懒值加载

/*懒值加载:
 *通过lazy标识的变量不会立即初始化,只有在首次对变量取值的时候才会初始化
 *lazy加载能够节省内存空间
 */
val sum=(a:Int,b:Int)=>println(a+b)
lazy result = sum(10,20) //不会初始化,调用时才会初始化
println(result)

3.8 高阶函数 – 套娃

//高阶函数:参数类型或者返回值类型为函数的方法/函数
val fun = (x:Int,y:Int,fun2:(Int,Int) => Int) => {fun2( x+y )}
//调用时简写见  3.3.2

/*案例1. 定义一个高阶函数,根据指定的逻辑对数组中的每个元素进行操作
 * val arr =Array("hello","word","python","scala")
 * 比如将数组的每个元素转成字符串长度[还可能进行其他操作]
 * val result = Array(5,4,6,5)
 */
object MapTest {
  def main(args: Array[String]): Unit = {
    val arr = Array("hello","word","python","scala")
    //todo 2.定义函数2,处理单个元素的逻辑
    val fun=(a:String)=>a.length
    //todo 3.调用函数,map-fun
    println(map(arr, fun).toBuffer)
    println(map2(arr, fun).toBuffer)
    //直接传入函数
    println(map(arr, (a:String)=>a.length).toBuffer)
    println(map2(arr, (a:String)=>a.length).toBuffer)
    //参数类型、括号、参数均可以省略
    println(map(arr,_.length).toBuffer)
    println(map2(arr,_.length).toBuffer)

  }
  //todo 1.定义函数,处理数组逻辑,遍历数组元素
  val map=(arr:Array[String],fun:(String)=>Int) => for(a <- arr) yield fun(a)
  def map2(arr:Array[String],fun:(String)=>Int)={for(a <- arr) yield fun(a)}

/**
 * 案例2. 定义高阶函数,根据指定的逻辑对数组进行统计
 * val arr = Array(5,4,6,5)
 * 比如统计总和[还可能统计总乘积等等]
 * val sum = 20
 *
 * 思路:
 * 遍历数组,第一个元素与第二个元素处理的结果作为下一次的第一个元素传出
 */
object ReduceTest {
  def main(args: Array[String]): Unit = {
    val arr = Array(5,4,6,5)
    val fun=(muti:Int,ele:Int) => muti*ele
    //调用函数
    println(reduce(arr, fun))
    //直接传入函数  简化
    println(reduce(arr, (muti:Int,ele:Int) => muti*ele))
    println(reduce(arr,_*_))
    println(reduce(arr,_+_))
  }
  //将数组中每个元素取出,传入到fun函数中调用
  def reduce(arr:Array[Int],fun:(Int,Int)=>Int):Int = {
    var muti:Int = arr(0)
    for(index <- 1 until arr.length){
      var ele =arr(index)
      muti=fun(muti,ele)
    }
    muti
  }
}

/**
 * 案例3. 定义一个高阶函数,根据指定的规则对数据进行过滤
 * val arr = Array(5,4,6,5)
 * 比如只需要偶数数据的结果
 * val result = Array(4,6)
 */
object FilterTest {
  def main(args: Array[String]): Unit = {
    val arr = Array(5,4,6,5)
    println(filter(arr))

    val fun=(a:Int)=> a % 2 == 0
    println(filter2(arr, fun).toBuffer)
  }
  //如果yield在返回内容前,会将不符合条件时的空值unit也返回
  val filter2=(arr:Array[Int],fun:Int=>Boolean)=>{
    //for(ele <- arr) yield if(fun(ele)) ele
    //将判断语句提为守卫,确定符合条件时才返回值
    for(ele <- arr if (fun(ele))) yield ele
  }
  def filter (arr:Array[Int]):util.ArrayList[Int]={
    val list = new util.ArrayList[Int]()
    for(index <- 0 until arr.length){
      if(arr(index)%2==0){
        list.add(arr(index))
      }
    }
    list
  }
}

/**
   * 案例4. 定义一个高级函数,按照指定的规则对数据进行分组
   * val arr = Array("zhangsan 20 shenzhen","lisi 30 shanghai","wangwu 30 shenzhen","zhaoliu 20 shenzhen")
   * 比如按照地区进行分区结果
   * shenzhen的结果: List["zhangsan 20 shenzhen","wangwu 30 shenzhen","zhaoliu 20 shenzhen"]
   * shanghai的结果: List["lisi 30 shanghai"]
   *
   * 思路:
   * 1.取出数组中每个元素并返回
   * 2.分组函数传入字符元素,及索引位置,以进行分组
   */
object GroupByTest {
  def main(args: Array[String]): Unit = {
    val arr = Array("zhangsan 20 shenzhen","lisi 30 shanghai","wangwu 30 shenzhen","zhaoliu 20 shenzhen")
    val fun=(ele:String)=>{ele.split(" ")(2)}

    println(groupby(arr, fun))
    println(groupby(arr, (s:String)=>s.split(" ")(2)))
    println(groupby(arr, _.split(" ")(1)))
  }
  def groupby(arr:Array[String],fun:String =>String)={
    //3.定义一个存放key的数组,用以区分不同key
    val result=new util.HashMap[String,util.List[String]]()
    //1.遍历获取指定元素
    for(ele <- arr){
      val key = fun(ele)
      //2.判定字符是否包含指定key
      if(result.containsKey(key)){
        val list = result.get(key)
        list.add(ele)
      }else{
        val list = new util.ArrayList[String]()
        list.add(ele)
        result.put(key,list)
      }
    }
    result
  }
}

第四章 面向对象编程

4.1 包-Package

--1.包的作用
 (1) 便于管理类
 (2) 便于对同名类进行区分
 (3) 对类的权限进行限制[private 包名]

--2.包的声明
  "域名反写 + 项目名 + 业务线 + 模块"
 (1).scala文件第一行中通过package关键字来声明包
 (2).scala文件中通过package关键字来声明包,可以在包中定义变量/class文件(在项目结构中不可见,需要在target文件中进行查看)
  
--3.包的导入
 (1) 导入包下的所有类: import 包名._
 (2) 导入包下某个类: import 包名.类名
 (3) 一次导入包下多个类: import 包名.{类名1,类名2}
 (4) 导入包下类的同时给类起别名: import 包名.{类名=> 别名}
 (5) 导入包下除开某个类的所有类: import 包名.{类名=>_ ,_}

 "Scala是一门完全面向对象的编程语言"
 
--4.包对象
 语法:pachage object 包名
 包对象中定义的非private的属性、方法、函数可以在包中任何地方使用[子包中不可以]
 
--5.Scala中包的声明说明:
 (1) 在同一个源码文件中,可以多次声明,声明的类在最后的包中,源码中的类所在的位置不需要和包的路径相同
 (2) scala中没有的语法都可以进行嵌套
     package可以使用{},在{}里面声明的类在此包中,{}之外声明的类不在此包中
 (3) scala中可以声明父包和子包,父包中的类,子类可以直接访问
 (4) scala中可以声明package类,但是无法声明变量和函数
 (5) scala为了弥补包的不足,使用了包对象的概念,在包对象中可以声明属性和函数

4.2 类-class

--scala中有class和object修饰的类
  --编译时区别:
  1.object: 在编译时会生成两个类文件,一个是当前类文件,一个是单例类文件
    例:Scala01_Class.class, Scala01_Class$.class
  2.class: 在编译时只会生成当前类文件
    例:Scala01_Class.class
    
  --编译时修饰区别:
  1.object: 由于scala中没有static,因此使用object修饰伴随着普通类而生成的单例对象,object中的属性和方法可以通过类名直接调用,类似于java中的静态类
  2.class: 修饰普通的类
  
  --object声明的类:伴生对象 
  --class声明的类:伴生类

--伴生类与伴生对象的声明
  --1.声明条件:
  	1.class与object在同一个.scala文件中
  	2.class与object的名称要一样
  	
  --2.声明方法:
    1.object+ 类名 声明伴生对象,直接通过类名调用属性和方法
    2.class + 类名 声明伴生类,必须new 对象调用属性和方法
    
  --3.特性:
    1.伴生类和伴生对象可以互相访问私有属性
    2.伴生对象的apply()方法创建伴生类对象
    
  --4.说明(至简原则):
  	1.声明类时{}中没有任何东西,{}可以省略
  	2.new 对象时,不需要传入参数,则()可以省略

//伴生类  @BeanProperty可以提供属性的get/set方法
class People(@BeanProperty var name:String,@BeanProperty var age:Int){
  private val address:String="shenzhen"
  People.sum(13,34) //可以调用伴生对象的私有属性和方法
}

object People{
  private def sum(x:Int,y:Int)=println(x+y)

  val people = new People("jason", 25)
  people.address  //可以调用伴生类的私有属性和方法

  //定义apply方法创建伴生类对象
  def apply(name:String,age:Int) = new People(name,age)
}

//通过apply方法创建对象
People.apply("jason",25)
//因scala可以自动识别apply,因此apply可以省略
People("jason",25)

4.3 属性-field(封装)

//1.声明语法
  [权限修饰符] val/var 属性名:属性类型 = 属性值
  private val name:String = "jason"
      
//2.属性初始值
  val:类似于final修饰的属性,不可以修改,因此必须有初始值
  var:可以使用默认初始值进行初始化*/
      
  val name:String = "jason"
  var age:Int = _
"各类型的初始化值:String_null,Int_0,Double_0.0,Char_'\u0000' 0"

//3.关于封装:
  (1) 封装:属性私有,提供公共的set/get方法
  (2) scala的属性在定义后之后,会默认的提供set/get方法
  	--默认的get方法的方法名:属性名
  	--默认的set方法的方法名:属性名_=
  (3) scala为了兼容java,提供了@BeanProperty注解——可以自动为属性生成get/set方法
    --@BeanProperty不能标注在私有的private 属性上

4.4 方法/函数

//1.方法声明
  [权限修饰符] def 方法名(参数名:参数类型,..):返回值类型 = {方法体}

//2.函数声明
  [权限修饰符] val 函数名 = (参数名:参数类型,..) => {函数体}

4.5 对象

--1.对象构建说明:
  val修饰的对象不可以改变地址值,可以改变对象属性的值
  var修饰的对象可以改变地址值和对象属性的值

--2.创建对象的几种方式:
 (1) new Class : val person = new Person
 (2) apply: Array.appply()/ Array()
 (3) classof
 (4) 反序列化
 (5) clone

4.6 构造器-construct

//1.scala中构造器有主构造器和辅助构造器两种
	构造器用来创建对象,在继承关系中,子类创建对象时会先调用父类构造器

//2.scala主构造器:
  (1) 语法:
 	 [private/..] [val/var] 属性名:属性类型[ = 默认值]
 	 public class Person(private val name:String="jason"){}
  (2) 说明:
     var/val修饰的属性可以在class外部使用[private修饰]
     不带var/val修饰的属性只可以在class内部使用
    
//3.scala辅助构造器:
  (1) 语法:
     def this(参数名:参数类型,...) = {}
  (2) 说明:
     在辅助构造器的"第一行"必须先调用其它构造器,且不能形成死循环
     辅助构造器不能直接构建对象,必须直接或者间接地调用"主构造器"

4.7 继承-extends

//1.scala中的继承与java中继承语法一样
	语法: 子类 extends 父类

//2.说明:
	a."final修饰的class"不能被继承
	b.父类"private修饰的属性和方法"不能被继承
	c.子类可以通过"override关键字"来重写属性/方法/函数
	d.子类重写父类属性时,只能重写"val修饰的属性"

4.8 抽象-abstract

//1.关键字 abstract  可以修饰类/属性/方法
	a. abstract class Person{}     --抽象类:不能创建对象的类 
    b. var|val name:String         --抽象属性:没有初始值的属性
    c. def sum(a:Int,b:Int):Int    --抽象方法:没有方法体的方法
    
//2.重写&继承
	a.抽象子类必须重写抽象父类中的所有抽象方法和抽象属性 才能实例化
	b.重写非抽象方法必须加"override"关键字,重写抽象方法可以不加
	c.子类调用父类的方法需要使用"super"关键字
	d.抽象属性重写只支持val 修饰的属性
	
//3.匿名对象
	val obj = new 抽象类{
		重写抽象的方法/属性
	}

4.9 特质-trait

4.9.1 Trait说明
  • Scala语言中,trait实际上用来代替java中接口
  • Scala中的类也是 单继承多实现(trait)
  • trait中可以定义抽象属性和抽象方法,也可以定义具体的属性和具体的方法
4.9.2 基本语法
//1.trait + 特质名{ 特质体 } 定义,可以定义抽象属性/方法和具体属性/方法
trait Demo{
    //具体属性
    val name:String="jason"
    //抽象属性
    val height:Int
    //具体方法
    def sum(x:Int,y:Int):Int=x+y
    //抽象方法
    def add(a:Int,b:Int):Unit  
}

//2.实现trait需重写抽象属性/方法才可实例化
class Test extends Test1 with Test2{
    override val height: Int = 12
    override def add(a: Int, b: Int): Unit = println(a+b)
}

//3.一个类可以 "混入" 多个特质 第一个实现/继承用extends,其它的用with
class Sub extends Parent1 with Parent2 with Parent3...

4.9.3 动态混入 mixin
//对象混入,让某个具体的对象实现trait特质的属性和方法
val people = new People with Test1 {
     override val height: Int = 180
     override def add(a: Int, b: Int): Unit = println(a+b)
}
//对象实现trait后即可使用trait中具体的属性和方法
     println("people name:" + people.name)

4.9.4 特质叠加
//特质叠加:将混入的多个trait中的冲突方法叠加起来
//问题:【钻石问题】当一个类实现了多个trait,且这多个trait中都有一个同名同参的方法,则子类直接调用会报错...

//解决方案1:子类重写父trait的同名同参方法
  --如果在方法体中super调用父trait的方法,则默认调用"最后一个with的方法体"

trait Test1{
    def demo(a:Int,b:Int):Unit=println("我是a+b:"+(a+b))
}
trait Test2{
    def demo(a:Int,b:Int):Unit=println("我是a*b:"+(a*b))
}
trait Test3{
    def demo(a:Int,b:Int):Unit=println("我是a/b:"+(a/b))
}
//子类重写此同名同参方法,如果在方法体中super调用父trait的方法,则默认调用最后一个with的方法体
class Demo extends Test1 with Test2 with Test3{
    override def demo(a:Int,b:Int):Unit={
    //println("重写后的:"+(a+b))
    super.demo(a,b)}  //调用Test3的(a/b)
}

//解决方案2:所有父trait同时继承一个爷trait,在爷trait中声明同名同参的方法[方法提取]
  --子类调用该方法时默认调用的最后一个trait

trait Grandpa{
    def sum(a:Int,b:Int):Unit=println("a+b:"+(a+b))
}
trait Parent1 extends Grandpa{
    override def sum(a:Int, b:Int):Unit=println("a*b:"+(a*b))
}
trait Parent2 extends Grandpa{
    override def sum(a:Int, b:Int):Unit=println("a/b:"+(a/b))
}
//子类实现父trait时,默认调用的方法是最后一个trait的
class Son extends Parent1 with Parent2  //Parent2的(a/b)

//*特质叠加的执行顺序【按照继承顺序从右向左依次执行】
举例:声明子父类关系的特质,子类实现特质对象
  - trait Grandpa 
  - trait Parent1 extends Grandpa 
  - trait Parent2 extends Grandpa
class Son extends Parent1 with Parent2

/*1. 子类Son混入多个特质时,scala会对所有的特质及其父特质按照一定的顺序进行排序并依次叠加
 *2. 子类Son的叠加顺序为:  Son -> Parent2 -> Parent1 -> Grandpa
 *3. 因此,当在子类中使用super直接调用父trait方法时,就近调用Parent2的方法
 *4. 如果子类想指定调用某个父trait的方法,可以使用:super[Parent1].sum()*/

class Son extends Parent1 with Parent2{
    override def sum(a: Int, b: Int): Unit = 
    super[Parent1].sum(a, b)
}

//*特质叠加的初始化顺序【按照继承顺序从左向右依次执行】
举例:声明子父类关系的特质,子类实现特质对象
  - trait Grandpa 
  - trait Parent1 extends Grandpa 
  - trait Parent2 extends Grandpa
class Son extends Parent1 with Parent2

--特质对象初始化顺序为:Grandpa -> Parent1 -> Parent2 -> Son

4.9.5 特质子类型
 -- this:Serializable[接口名] =>  //表示实现该trait必须实现或者继承该指定[接口名]

//需求:编辑一个trait,使其它子类继承该trait后实现自己写入磁盘的功能
object SelfObject {

  trait IOObject{
    this:Serializable => //提示实现该trait的子类必须实现serializable接口
      
    //定义读方法
    def read()={
      val ois = new ObjectInputStream(new FileInputStream("d:/abc.txt"))
      val obj = ois.readObject()
      ois.close()
      obj
    }
	//定义写方法
    def write()={
      val oos = new ObjectOutputStream(new FileOutputStream("d:/abc.txt"))
      oos.writeObject(this)
      oos.flush()
      oos.close()
    }
  }
  class Person(val name:String,var age:Int) extends IOObject with Serializable

  def main(args: Array[String]): Unit = {
    val person = new Person("jason",25)
    person.write()
  }
}

4.10 扩展

4.10.1 类型检查和转换
--1.类型判断
  java中判断类型:  对象 instanceof B
  scala中判断类型: 对象.isInstanceOf[类名]

--2.类型强转
  java中强转: (类型)对象
  scala的强转: 对象.asInstanceOf[类名]

--3.获取对象的class
  java获取对象的class形式:  对象.getClass
  scala获取对象的class形式: 对象.getClass

--4.获取类的class
  java中获取类的class形式:  类名.class
  scala中获取类的class形式: classOf[类名]

*4.10.2 枚举类与应用类
//1.枚举类 需要继承Enumeration
object Color extends Enumeration {
    val RED = Value(1, "red")
    val YELLOW = Value(2, "yellow")
    val BLUE = Value(3, "blue")
}

//2.应用类 需要继承App 官方提示有BUG不建议使用
object Test20 extends App {
    println("App 有Bug喔!");
}

*4.10.3 Type
//type关键字可以给数据类型起别名,在之后的调用中可以直接使用别名
--1.语法: type 别名 = 类名
--2.案例:
        type S = String 				//给String起别名为S
        var name : S = "jason"			//使用时可以直接使用别名S
        def say() : S = "hello jason"

第五章 集合

5.1 概述

--1.Scala中所有的集合扩展自【trait Iterable】特质,分为三大类:序列Seq、集Set、映射Map
--2.大部分集合可以分为可变集合和不可变集合两类
	scala.collection.immutable  --不可变
	scala.collection.mutable	--可变
	
  不可变集合:集合的长度不可变,增/删时会创建一个新的集合,原集合不变
  可变集合:集合的长度可变,增/删时可以在原集合基础上,也可以创建新的集合
  
--3.关于集合增/删元素的说明
	val arr1 = Array[Int](1,2,3,4,5)
  (1) +/++ | -/-- => +/-代表对单个元素的增删,++/--代表对一个集合的增删
	arr1.:+(3) => arr2(1,2,3,4,5,3)
	
  (2) 前:/后: +:/:+ => 前:代表新增的元素放在原集合最后,后:代表新增的元素放在原集合的前面
	arr1.+:(0) => arr2(0,1,2,3,4,5)
	
  (3) +=/-=/++=... => 在原集合的基础上进行增删操作
    arr1.+=(0) => arr1(1,2,3,4,5,0)

5.2 Array(有序,可重复)

5.2.1 不可变Array
//1.创建 import scala.collection.mutable
  (1) new Array[元素类型](数组长度)

  (2) Array[.apply][元素类型](初始元素,初始元素...)
	val arr1 = Array[Int](1,2,3)
	val arr2 = new Array[Int](10)

//2.查  array(角标)
	println(arr1(2))  //3
	println(arr1.toBuffer)  //ArrayBuffer(1,2,3)

//3.增  +: | :+ | ++ | ++:
	val arr3 = arr1.+:(1)  //(1,1,2,3)
	val arr4 = arr1.++(Array(0,5,7))  //(1,2,3,0,5,7)

//4.改 array(角标) = 值  原集合更改
	arr1(0) = 7  //(7,2,3)

//5.删  没有

//6.遍历
	for(i <- arr1) println(i)

//7.可变数组与不可变数组的转换
	val arr5 = arr1.toBuffer
	val arr6 = arr5.toArray

5.2.2 可变ArrayBuffer
//1.创建 import scala.collection.mutable.ArrayBuffer
  (1) new ArrayBuffer[元素类型](数组长度)

  (2) ArrayBuffer[.apply][元素类型](初始元素,初始元素...)
	val arr1 = ArrayBuffer[Int](1,2,3)
	val arr2 = new ArrayBuffer[Int](10)

//2.查  array(角标)
	println(arr1(2))  //3
	println(arr1)  //ArrayBuffer(1,2,3)

//3.新集合增   +: | :+ | ++ | ++: |
//  原集合增  +=: | += | ++= | ++=:

	val arr3 = arr1.+:(1)  //(1,1,2,3)
	val arr4 = arr1.++(Array(0,5,7))  //(1,2,3,0,5,7)

//4.改 array(角标) = 值  原集合更改
	arr1(0) = 7  //(7,2,3)

//5.删   - | -- | -= | --=
	val arr5 = arr1.--(Array(0,2,4))  //删除集合中没有的元素不会报错
	arr1.-=(3)

//6.遍历
	for(i <- arr1) println(i)

//7.可变数组与不可变数组的转换
	val arr5 = arr1.toBuffer
	val arr6 = arr5.toArray

5.3 List(有序,可重复)

5.3.1 不可变 List
//1.创建 import scala.collection.mutable
  --1. List[.apply][元素类型](初始元素,初始元素...)

  --2. List[元素类型] = Nil  --创建空的list(必须指定元素类型)
	val list1 = List[Int](2,4,6,8)
	val list0:List[Int] = Nil

//2.查  list(角标)
	println(list1(2))  //6
	println(list1)  //List(2,4,6,8)

//3.增  +: | :+ | ++ | ++: | :: | :::
  --1.添加单个元素
	val list2 = 0 :: list1

  --2.连续添加多个元素,"最后必须是一个集合接收"
	val list3 = 0 :: (1, 3) :: (4, 6) :: list1 //此时(1,3)是一个元组

  --3.可以使用Nil接收元素
	val list4 = 0 :: 1 :: 2 :: Nil  //(0,1,2)

//4.改  updated(a,n) 将角标为a的元素值改为n,并创建一个新的集合接收
	val list5 = list1.updated(1,10)  //(2,10,6,8)
	//list4(1)=2  不能通过这种方式更改

//5.删  --没有

//6.遍历
	for(i <- list1) println(i)

//7.可变集合与不可变集合的转换
	val list6 = list1.toBuffer
	val list7 = list6.toArray

5.3.2 可变ListBuffer
//1.创建 import scala.collection.mutable.ListBuffer  --不能使用Nil
  --1. ListBuffer[.apply][元素类型](初始元素,初始元素...)

  --2. new ListBuffer[元素类型]()
	val list1 = ListBuffer[Int](2,4,6,8)
	val list0 = new ListBuffer[Int]()

//2.查  list(角标)
	println(list1(2))  //6
	println(list1)  //List(2,4,6,8)

//3.新集合增   +: | :+ | ++ | ++: |
//  原集合增  +=: | += | ++= | ++=:  没有:: :::方法
	val list2 = list1.++(List(2,4,6))

//4.改 
  --1.list(n) = a 在原集合上修改
	list1(0) = 5

  --2.updated(n,a) 修改并创建新集合
	val list3 = list1.updated(0,3)

  --3.update(n,a) 在原集合上修改
	list1.update(2,3)

//5.删  - | -- | -= | --=
	list1.--=(3)

//6.遍历
	for(i <- list1) println(i)

//7.可变集合与不可变集合的转换
	val list6 = list1.toBuffer
	val list7 = list6.toArray

5.4 Set(无序,不可重复)

5.4.1 不可变Set
//1.创建 Set[.apply][元素类型](初始元素,初始元素...)
	val set = Set[Int](1,2,3,4,5,6,7)

//2.增  + | ++ | ++: 因元素为无序的,因此只需使用+|++即可
	val set2 = set.+(20)
	val set3 = set.++(List(10,20,30))  //添加已有元素时不会报错,也不会添加成功

//3.查  
  --1.set(n) 查询set中是否有元素n
	println(set(0))  //false

  --2.println(set)   //Set(5, 1, 6, 2, 7, 3, 4)

//4.改  --没有

//5.删  - | --
	val set4 = set.-(4)
	val set5 = set.--(List(2,3))

//6.遍历
	for(i <- set) println(i)

//7.可变集合与不可变集合的转换  --没有

5.4.2 可变Set
//1.创建 import scala.collection.mutable
  --mutable.Set[.apply][元素类型](初始元素,初始元素...)
	val set = Set[Int](1,2,3,4,5)

//2.增 元素无序,不可重复
  --1. + | ++ | ++: | += | ++=  
	val set2 = set.+(20)
	val set3 = set.++(List(10,20,30))  //添加已有元素时不会报错,也不会添加成功
	set.+=(7)
	set.++=(List(3,5,7))

  --2.update(n,Boolean)  true增加  false删除
	set.update(7,false)

//3.查  
  --1.set(n) 查询set中是否有元素n
	println(set(0))  //false

  --2.println(set)   //Set(5, 1, 6, 2, 7, 3, 4)

//4.改  --没有

//5.删  - | -- | -= | --=
	val set4 = set.-(4)
	val set5 = set.--(List(2,3))
	set.-=(4)
	set.--=(List(2,3))

//6.遍历
	for(i <- set) println(i)

//7.可变集合与不可变集合的转换  --没有

5.5 Map(K-V键值对)

5.5.1 不可变Map
//1.不可变Map为有序的(按添加顺序有序)

//2.创建  即多个元组的集合
  --1.Map[K的类型,V的类型]((K,V),(K,V),...)
	val map1 = Map[String,Int](("zhangsan",20),("lisi",30),("wangwu",40))

  --2.Map[K的类型,V的类型](K -> V,K -> V,...)
    val map2 = Map[String,Int]("zhangsan"->20,"lisi"->30,"wangwu"->40)

//3.增  + | ++ | ++:
	val map3 = map1.+(("zhaoliu",30))  //单个添加按添加顺序排序
	val map4 = map1.++(Map("aa"->20,"bb"->30))  //添加map顺序 ?

//4.查
  --a.直接使用 "map(key)" 的方式获取,如果key找不到会报错
    println(map4("scala"))  //10

  --b.使用 "map.get(key)" 的方式获取,返回结果为 some(value),如果key找不到返回None
    println(map4.get("scala"))  //Some(10)
    println(map4.get("scala1")) //None

    * Option: 【提醒外部值有可能为空,提供外部进行处理】
    *   Some: 代表非空,数据封装在Some中
    *   None: 代表空值

  --c.使用 "map.getOrElse(key,默认值)" 的方式获取,如果key找不到返回默认值
    println(map4.getOrElse("scala1", 100))  //100

//5.改
  --updated(key,n)  将key对应的value值改为n
	val map5 = map1.updated("zhangsan", 30)

//6.删  - | --
    val map6 = map4.-("scala")
    val map7 = map4.--(List("scala", "java"))

//7.遍历
  --a.全部遍历
	for (e <- map4) println(e._1,e._2)

  --b.获取所有keys
	for(e <- map4.keys) println(e)

  --c.获取所有values
	for(e <- map4.values) println(e)

5.5.2 可变Map
import scala.collection.mutable

//1.可变Map为无序的  

//2.创建  即多个元组的集合
  --1. mutable.Map[K的类型,V的类型]((K,V),(K,V))
	val map1 = Map[String,Int](("jason",25),("qiang",20))

  --2. mutable.Map[K的类型,V的类型](K -> V,K -> V)
    val map2 = Map[String,Int]("jason"->25,"qiang"->20)

//3.增  + | += | ++ | ++: | ++= | put
	val map3 = map1.+(("yan",18)) 
	val map4 = map1.++(Map("aa"->20,"bb"->30))
	map1.++=(Map("aa"->20,"bb"->30))
	map1.put("cc",18)  //put添加的是两个变量充当k v

//4.查
  --a.直接使用 "map(key)" 的方式获取,如果key找不到会报错
    println(map("scala"))  //10

  --b.使用 "map.get(key)" 的方式获取,返回结果为 some(value),如果key找不到返回None
    println(map.get("scala"))  //Some(10)
    println(map.get("scala1")) //None

    * Option: 【提醒外部值有可能为空,提供外部进行处理】
    *   Some: 代表非空,数据封装在Some中
    *   None: 代表空值

  --c.使用 "map.getOrElse(key,默认值)" 的方式获取,如果key找不到返回默认值
    println(map4.getOrElse("scala1", 100))  //100

//5.改
  --a. map(key) = value
	map("key")=100
  --b. update(key,n)  在原map中,将key对应的value值改为n
    map.update("jason",1000)
  --c. updated(key,n)  将key对应的value值改为n,存到新map中
	val map5 = map.updated("jason", 30)

//6.删  - | -- | -= | --= | remove
    val map6 = map.-("scala")
    val map7 = map.--(List("scala", "java"))
	map.-=("jason")
	map.--=(List("jason","aa")
	map.remove("bb")

//7.遍历
  --a.全部遍历
	for (e <- map) println(e._1,e._2)

  --b.获取所有keys
	for(e <- map.keys) println(e)

  --c.获取所有values
	for(e <- map.values) println(e)

5.6 Tuple

//1.说明
	(1)()内,用","分割,形成一个元组
	(2) 一个元组内最多有22个元素
	(3) 元组一旦创建,长度和内容都不可以修改
	(4) 元组内各元素类型可以不同
 
//2.创建  (a,b,(c1,c2...)...)
	val t1 = ("jason",25,"帅")
	val t2 = (1,2,3,(1,2,(3,4,5)))

//3.查  ._n n为角标,从1开始
	println(t2._4._3._3)  //5

//4.集合嵌套元组
val array: Array[(String, Int, Double)] = Array[(String, Int, Double)](
    ("jason", 25, 180.0),
    ("qiang", 26, 178.8),
    ("yan", 26, 199.9))
for(a <- array) println(a)

//案例:对象封装--样例类

//改善前案例
val data = Array[(String,(String,(String,Int)))](
    ("深圳",("宝安区",("张三",18))),
    ("深圳",("宝安区",("李四",18))),
    ("深圳",("宝安区",("王五",18))),
    ("深圳",("宝安区",("赵六",18)))
)

for(i<- data) println(i._2._2._1)

//获取数据时很难通过表达式了解真正的含义,因此,可采用样例类的方式

//改善后案例
  case class City(name:String,area:Area)
  case class Area(name:String,person:Person)
  case class Person(name:String,age:Int)

val data2 = Array[School](
    City("深圳",Area("宝安区",Person("张三",20))),
    City("深圳",Area("宝安区",Person("李四",20))),
    City("深圳",Area("宝安区",Person("王五",20))),
    City("深圳",Area("宝安区",Person("赵六",20)))
)
for( t <- data2) println(t.area.person.name)

5.7 常用函数

5.7.1 基本属性
//集合的基本属性  集合->基本类型
--length -> Int  "获取集合长度"  
	list.length  --3
--size -> Int  "获取集合长度"  
	list.size  --3
--isEmpty -> Boolean  "判断集合是否为空"  
	list.isEmpty  --true
--contains(n) -> Int  "判断集合是否包含元素n"  
	list.contains("a")  --false
--mkString("x") -> String  "以x为分割符将集合转为字符串"  
	list.mkString("-")  --1-5-6-8

5.7.2 衍生集合
//集合的衍生集合  集合->集合 --->可变集合在原集合处理,不可变集合需生成新集合
--distinct -> List  "集合元素去重" 
	val list2 = list.distinct
--drop(n) -> List  "删除集合前n个元素" 
	val list2 = list.drop(2)
--dropRight(n) -> List  "删除集合后n个元素"
	val list2 = list.dropRight(2)
--head -> 元素类型  "返回集合第1个元素" 
	val head: String = list.head
--last -> 元素类型  "返回集合最后1个元素" 
	val last: String = list.last
--init -> List  "获取除最后1个元素的所有元素集合"
	val list2 = list.init
--tail -> List  "获取除第1个元素的所有元素集合"
	val list2 = list.tail
--reverse -> List  "反转"
	val list2 = list.reverse
--take(n) -> List  "获取前n个元素的所有元素集合"
	val list2 = list.take(2)
--takeRight(n) -> List  "获取后n个元素的所有元素集合"
	val list2 = list.takeRight(2)
--zip(list2) -> List  "拉链,将两个集合合并为元组元素存放在新集合中,长度不一以短为准"
	val words = List("aa","bb","cc","dd")
	val counts = List(10,20,30)
	val list = words.zip(counts)  //List((aa,10), (bb,20), (cc,30))
--unzip(list2) -> List(List)  "反拉链 生成元组,元组中有两个集合"
	val list = List((aa,10), (bb,20), (cc,30))
	val list2 = list.unzip  //(List(aa, bb, cc),List(10, 20, 30))
--intersect -> List  "交集"
	val list3 = list1.intersect(list2)
--diff -> List  "差集 A有B没有的"
	val list3 = list1.diff(list2)
--union -> List  "并集"
	val list3 = list1.union(list2)
--slice(a,b) -> List  "获取子集合,对应角标[a,b)" 
	val list2 = list1.slice(1,3)
--sliding(a,b) -> List  "开窗,a为开窗大小,b为滑动长度"
	val list = List(10,20,30,40,50,60)
	val list2 = list.sliding(3,2)
	println(list2.toBuffer)  
  //ArrayBuffer(List(10, 20, 30), List(30, 40, 50), List(50, 60))

5.7.3 初级函数
//1.基本聚合函数
--max -> Int  "获取元素最大值,按字典序排"
	val max = list.max
--min -> Int  "获取元素最小值,按字典序排"
	val min = list.min
--sum -> Int  "获取元素平均值"
	val sum = list.sum
--product -> Int  "获取元素总乘积"
	val product = list.product

//2.排序
--sorted -> List  "按照元素排序,默认升序排序"
	println(list.sorted)	//升序
    println(list.sorted.reverse)	//降序
--sortBy(fun(x:集合类型 => Any)) -> List  "指定排序字段,默认升序排序"
	println(list.sortBy(_._2))
--sortWith(fun(x,y => Boolean)) -> List "指定排序规则 小于为升序,大于为降序"
	println(list.sortWith((a, b) => a > b))

5.7.4 高级函数
//1.map 对集合的每个元素进行操作,返回个数与原集合个数相同
--map(fun(x:元素类型 => Any)) --> 一对一  

//2.filter 过滤,针对集合的每个元素,指定判断条件,true的返回
--filter(fun(x:元素类型 => Boolean))  

//3.foreach 对集合的每个元素进行操作,无返回值
--foreach(fun(x:元素类型 => Unit)) --> 一对一 

//4.flatten 无参,拍平,将集合嵌套集合压平为一个集合
--flatten(fun(集合[集合[元素类型]]) => 集合[元素类型]) --> 一对多

//5.flatMap 相当于map+flatten,先对逐个元素处理,再拍平
--flatMap(fun(集合[集合[元素类型]]) => 集合[元素类型]) --> 一对多  

//6.groupBy 分组,指定分组的字段  返回值类型为Map
--groupBy(fun(x:元素类型 => Any)) --> 多对一

//7.reduce 指定聚合规则,将所有元素聚合为一个值,从左到右计算
--reduce(fun(sum:元素类型,a:元素类型 => 元素类型)) --> 多对一

//8.reduceRight 指定聚合规则,将所有元素聚合为一个值,从右到左计算
--reduceRight(fun(a:元素类型,sum:元素类型 => 元素类型)) --> 多对一

//9.fold 指定聚合规则,将所有元素聚合为一个值,从左到右计算,初始的sum为n
--fold(n)(fun(sum:元素类型,a:元素类型 => 元素类型))) --> 多对一

//10.foldRight 指定聚合规则,将所有元素聚合为一个值,从右到左计算,初始的sum为n
--fold(n)(fun(a:元素类型,sum:元素类型 => 元素类型))) --> 多对一

//案例  关于高级函数的计算
val list = List[String]("spark","hello","hadoop","scala","flume","kafka")
//todo 1.map 一对一
println(list.map(_.length))  //List(5, 5, 6, 5, 5, 5)

//todo 2.filter 过滤,true的返回
println(list.filter(_.contains("s")))  //List(spark, scala)

//todo 3.foreach 无返回值
list.foreach(println(_))

//todo 4.flatten 压平  一对多  将集合嵌套集合压平为一个集合
val list1 = List(List(1, 2, 3), List(4, 5, 6), List(7, 8))
println(list1.flatten)  List(1, 2, 3, 4, 5, 6, 7, 8)

//todo 5.flatMap  map + flatten
val list2 = List(("hello,world"), ("jason,scala,hadoop"))
val list3 = list2.flatMap(x => x.split(","))
println(list3)  //List(hello, world, jason, scala, hadoop)

//todo 6.groupBy  多对一
val list4 = List("aa", "bb", "cc", "dd", "bb", "aa")
println(list4.groupBy(x => x))
//Map(bb -> List(bb, bb), cc -> List(cc), dd -> List(dd), aa -> List(aa, aa))

val list5 = List((1,"aa"),(1,"bb"),(3,"dd"),(4,"rr"),(3,"ff"))
println(list5.groupBy(_._1))
//Map(4 -> List((4,rr)), 1 -> List((1,aa), (1,bb)), 3 -> List((3,dd), (3,ff)))

//todo 7.reduce  多对一
println(list1.flatten.reduce(_+_))  //36

//reduceRight
println(list1.flatten.reduceRight(_+_))  //36

//todo 8.fold 多对一  与reduce类似,在第一个参数中指定初始值
println(list1.flatten.fold(100)(_ + _))  //136

//foldRight
println(list1.flatten.foldRight(100)(_ + _))  //136

*5.8 Queue(先进先出)

//1.不可变队列 import scala.collection.immutable.Queue
  --1.创建 
    Queue[.apply][元素类型](初始元素,初始元素...)
      val queue = Queue[Int](1,2,3)

  --2./+: | :+ | ++ | ++: | enqueue | dequeue
    val queue2 = queue.enqueue(8,9) //只能添加一个元素 (1,2,3,(8,9))
	val value = queue.dequeue  // 1
	
//2.可变队列 import scala.collection.mutable
  --1.创建
	mutable.Queue[.apply][元素类型](初始元素,初始元素...)
	val queue0 = mutable.Queue[Int](10,20,30)

  --2./+: | :+ | ++ | ++: | += | +=: | ++= | enqueue | dequeue
	queue7.enqueue(80,90) 
    println(queue0) //(10,20,30,80,90)
    println(queue0.dequeue)  //10
    println(queue0)  //(20,30,80,90)

5.9 并行函数

def main(args: Array[String]): Unit = {
    val list = List(10,20,30,40)

    list.foreach(x=>{
        println(Thread.currentThread().getName)
        println(x)
    })
    println("+"*100)
    //使用par调用多线程并行运行
    list.par.foreach(x=>{
        println(Thread.currentThread().getName)
    })
}

第六章 模式匹配

6.1 基本语法

/* 1.基本语法 
模式匹配  值/函数 match{
    case 匹配条件1 => 执行语句1
    case 匹配条件2 => 执行语句2
    ...
    case _ => 其它的执行语句 */

val prt: Any = StdIn.readLine("请输入一个任意值:")
prt match{
    case "hello" => println("hello...")
    case "jason" => println("jason...")
    case x:String => println(s"${x} 是一个String类型")
    case x:Int => println(s"${x} 是一个Int类型")
    case _ => println("啥也不是...")
    
/* 2.模式守卫  在case语句后使用条件判断,判断为true的执行执行语句
模式匹配  值/函数 match{
    case 匹配条件1 条件判断1 => 执行语句1
    case 匹配条件2 条件判断2 => 执行语句2
    ...
    case _ => 其它的执行语句 */
    
val word: String = StdIn.readLine("请输入一个任意值:")
word match {
    case x: String if x.contains("j") => println(s"${x}里包含有j")
    case x: String if x.contains("s") => println(s"${x}里包含有s")
    case x: String if x.startsWith("y") => println(s"${x}以s开头")

6.2 模式匹配类型

6.2.1 常量
"1.对值类型进行匹配"
    //如果要匹配类型,则匹配的变量类型必须定义为Any
  val list: List[Any] = List("jason", 12, 0.0, true, 'k')
    //随机生成list长度以内的整数
  val index = Random.nextInt(list.length)
  val v: Any = list(index)
  v match{
      case x:Int if x % 2 == 0 => println(s"${x} 是一个整数,而且是一个偶数")
      case x:String if x.startsWith("j") => println(s"${x} 是一个以s开头的字符串")
      case x:Double => println(s"${x} 是一个double类型的元素...")
      case x:Boolean => println(s"${x} 是一个Boolean类型的元素...")
      case x:Char => println(s"${x} 是一个Char类型的元素...")
      case _ => println("这不在我考虑的范围内...")
  }

"2.对值进行匹配"
  val list1: List[Int] = List(5,8,6,13,15)
    //随机生成list长度以内的整数
  val index1 = Random.nextInt(list1.length)
  val v1: Any = list1(index)
  v1 match{
      case x:Int if(x % 2 == 0) => println(s"${x} 是一个偶数")
      case x:Int if(x % 5 == 0) => println(s"${x} 可以被5整除")
      case _ => println("这是一个神奇的数字...")
  }

6.2.2 数组
//todo 1.可以对数组的 "元素" 个数、类型、值...进行判断
val array: Array[Int] = Array(1, 5, 8, 6, 4, 9)
array match{
    case Array(a,b,c) => println(s"数组中有三个元素,分别是:${a} ${b} ${c}")
    case Array(a,_*) => println(s"数组中至少有一个元素 ${a}")
    case Array(a:Int,b:Int,_*) => println(s"数组中至少有两个Int类型的元素 ${a} ${b}")
    case Array(1,_*) => println("数组是以数字1开头的")
}

//todo 2.可以对整个数组的类型进行判断

//如果需要对数组类型进行判断,需要将变量类型改为Any,否则case语句无法匹配会报错
//val array2: Array[Int] = Array(1, 5, 8, 6, 4, 9)
val array2: Any = Array[Int](1, 5, 8, 6, 4, 9)
array2 match{
    case array:Array[Any] =>println("Any数组")
    case array:Array[Int] =>println("整型数组")
    case array:Array[Double] =>println("Double数组")
    case array:Array[String] =>println("字符串数组")
}

6.2.3 List (泛型擦除)
val list:Any = List[Int](1, 5, 3, 5, 2)

//todo 1.对集合的元素进行判断
list match{
    case List(a,b,c) => println(s"数组中有三个元素,分别是:${a} ${b} ${c}")
    case List(a,_*) => println(s"数组中至少有一个元素 ${a}")
    case List(a:Int,b:Int,_*) => println(s"数组中至少有两个Int类型的元素 ${a} ${b}")
    case List(1,_*) => println("数组是以数字1开头的")
}

//todo 2.对集合类型进行判断
  //泛型擦除 集合在编译的时候会去掉泛型  因此匹配类型时只要是List都会匹配成功
  // 【类似的存在泛型擦除的有List、queue......】
list match{
    case x:Array[Any] => println("我是一个数组")
    case x:List[Any] => println("Any集合")  //只要是list就会匹配成功
    case x:List[Int] =>println("整型集合")
    case x:List[Double] =>println("Double集合")
    case x:List[String] =>println("字符串集合")
}

//todo 3.List独有的 :: | :::
list match{
    case x :: Nil => println(s"集合中只有一个元素${x}")
    case a :: b :: Nil => println(s"集合中有两个元素${a} ${b}")
    //:: 最后必须以list接收,tail(或其他任意值)此处充当一个类-tail的集合
    case (x:Int) :: tail => println(s"集合以int类型的${x}开头,剩下的元素为${tail}")
    case (x:Int) :: middle :: tail => println(s"集合以int类型的${x}开头,第二个元素为${middle},剩下的元素为${tail}")
    case (x:String) :: tail => println(s"集合以String类型的${x}开头,剩下的元素为${tail}")
    case _ => println("这...是我没见过的奇迹")
}

6.2.4 Tuple
val t1: (Any,Any,Any) = ("jason", 25, 176.3)

//todo 1.匹配元组,匹配条件必须与元组长度一致
t1 match{
    case (a,b,c) => println(s"元组元素有三个,分别为:${a} ${b} ${c}")
    //case (a,_*) => println("")  //匹配条件与元组长度必须一致
}

//todo 2.类型判断
//当变量定义为Any类型时,匹配条件中Any和正确的数据类型都可以匹配成功
t1 match{
    case (a:Int,b:Int,c:Int) => println(s"${a}:Int,${b}:Int,${c}:Int")
    case (a:String,b:Int,c:Double) => println(s"${a}:字符串,${b}:Int,${c}:Double")
    case (a:Any,b:Any,c:Any) => println(s"${a}:Any,${b}:Any,${c}:Any")
}

//todo 3.对象元组匹配
val t2: Array[(String, (String, (String, Int)))] = Array[(String, (String, (String, Int)))](
    ("深圳", ("宝安区", ("张三", 20))),
    ("深圳", ("宝安区", ("李四", 18))),
    ("深圳", ("宝安区", ("王五", 38))),
    ("深圳", ("宝安区", ("赵六", 26)))
)
//如果要遍历某个字段
t2.map(_._2._2._1).foreach(println)
  //但是不方便理解取值字段含义,因此可以使用模式匹配增强代码表达性
t2.map(x =>
       x match{
           case (city,(area,(name,age))) => name
       }
      ).foreach(println)
}

6.3 样例类

--1.样例类
  (1) 语法:case class类名([val/var] 属性名:属性类型,...)
  (2) 定义样例类时如果不写val/var,则默认是val
  (3) 样例类实际上相当于创建了一个伴生类和一个伴生对象
     object Person | class Person(...)

--2.样例对象
  (1) 语法:case object 名称
  (2) 一般用样例对象来做枚举值
  
--3.普通类实现模式匹配
  (1) 创建伴生类和伴生对象
  (2) 在伴生对象中定义unapply方法

案例1:样例类匹配模式
	//todo 1.1 创建样例类
  case class Person(name:String,age:Int)
	//1.2 创建样例类对象
  val person: Person = Person("jason", 25)
	//1.3 一般的属性值输出方式
  println(person.age)
  println(person.name)
	//1.4 样例类的模式匹配
  person match{
    case Person(name,age) => println(s"name:${name},age:${age}")
  }

案例2:样例对象模式匹配
	//todo 2 创建样例对象
  abstract class Parent
	//2.1 样例对象继承Parent,则样例对象即为Parent的子对象
  case object Man extends Parent
  case object Woman extends Parent
	//2.2 自定义方法 需传入Parent的一个对象
  def get(parent:Parent)= println(parent)
	//2.3样例对象直接当做枚举类来使用
  get(Man)
  get(Woman)

案例3:普通类实现模式匹配
	//3.1 创建伴生类和伴生对象
  class People(val name:String,val age:Int)
  object People{
    //3.2 定义unapply方法
    def unapply(arg: People): Option[(String, Int)] = {
      if(arg == null) 
        None
      else 
        Some(arg.name,arg.age)
    }
  }
	//3.3 普通类的对象实现模式匹配
  val people = new People("jason", 26)
  people match{
    case People(name,age) => println(s"name:${name}, age:${age}"
  }

6.4 定义变量时模式匹配

object MatchVariable {
  def main(args: Array[String]): Unit = {
    //定义变量的时候使用模式匹配(其实就是模仿后面类型的结构)

    //1.元组
    //一般情况
    val t1 = ("jason",25,"深圳市")
    //模式匹配
    val (name,age,address) = ("jason",25,"深圳市")
    println(name,age,address)
    //如果匹配元素不使用,可以使用_代替
    val (name1,_,_) = ("jason",25,"深圳市")
    println(name1)

    //2.数组
    //一般情况
    val array: Array[Int] = Array[Int](1, 2, 3, 4)
    //模式匹配
    val Array(x,_*) = Array[Int](1, 2, 3, 4)
    println(x)
    val Array(a,b,c,_) = Array[Int](1, 2, 3, 4)
    println(a,b,c)

    //3.List
    //一般情况
    val list: List[Any] = List[Any](5, 6, 8, "da", 0.0, true)
    //模式匹配
    val List(l:Int,_*) = List[Any](5, 6, 8, "da", 0.0, true)
    println(l)

    val head :: tail = List[Any](5, 6, 8, "da", 0.0, true)
    println(head)  //5
    println(tail)  //List(6, 8, da, 0.0, true)

    //4.Map  for循环的模式匹配
    val map = Map("jason"->25,"qiang"->18)
    for((k,v)<-map){
      println(s"${k},${v}")
    }
  }
}

6.5 偏函数

//偏函数1)语法:IN为参数类型  OUT为返回值类型
    val 函数名:PartialFunction[IN,OUT]={
        case 匹配条件1:In => OUT
        case 匹配条件2:In => OUT
        ...
    }2)偏函数,一般在数据结构中比较复杂的元组时,将偏函数作为参数传递给map/flatmap...

object PartialFunction {
  def main(args: Array[String]): Unit = {
    //1.偏函数的基本使用
    val fun:PartialFunction[Any,Int]={
      case x:String => 20
      case x:Int => 30
      case x:Double => 40
      case x:Boolean => 10
      case _ => 0
    }
    println(fun(0.0))
    println(fun("jason"))

    //2.获取指定字段时可以使用偏函数
    val t: Array[(String, (String, (String, Int)))] = Array[(String, (String, (String, Int)))](
      ("深圳", ("宝安区", ("张三", 20))),
      ("深圳", ("宝安区", ("李四", 18))),
      ("深圳", ("宝安区", ("王五", 38))),
      ("深圳", ("宝安区", ("赵六", 26)))
    )
    //todo 方式1.定义偏函数来获取name字段
    val getName:PartialFunction[(String, (String, (String, Int))),String]={
      case (city,(area,(name,age))) => name
    }
    //调用偏函数
    t.map(getName(_)).foreach(println)

    //todo 方式2.直接在map{}中定义偏函数
    t.map{
      case (city,(area,(name,age))) => name
    }.foreach(println)
  }
}

第七章 异常处理

7.1 Java异常处理流程

public class ExceptionDemo {
    public static void main(String[] args) {
		//使用try来包裹需要抓取异常的代码
        try {
            int a = 10;
            int b = 0;
            int c = a / b;
        }catch (ArithmeticException e){
            // catch时,需要将范围小的写到前面
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        //最终执行一次,一般用来关闭资源
        }finally {
            System.out.println("finally");
        }
    }
}

7.2 Scala异常处理流程

7.2.1 异常处理方式
scala中异常处理的三种方式:
--方式1:
  --try{...}catch{case e Exception => ...}finally{...}
	类似于java中异常处理流程
	
--方式2:
  --Try(...).getOrElse(默认值)
    如果Try包裹的代码报错,则返回默认值,否则正常运行
    一般用于获取外部链接的时候使用

--方式3:
  --try{Left(...)}catch{case e Exception => Right(...)}
	正常时执行Left,异常时执行Right

7.2.2 案例
object Exception {
  def main(args: Array[String]): Unit = {
    //todo 方式1 try catch finally
    try{
      println(1/0)
    }catch{
      case e:Exception=>println(e.getMessage + s"${e}--这样的参数我不能接受呦~")
    }finally {
      println("Best Wishes...")
    }

    //todo 方式2 Try().getOrElse()
    val result = Try(1/0).getOrElse(0)
    println(result)

    //案例:数据过滤
    val datas = List[String](
      "1,zhangsan,20,shanghai",
      "2,lisi,20,shanghai",
      "3,wangwu,20,shanghai",
      "zhaoliu,20,shanghai",
      "4,qianqi,shanghai",
      "5,wangba,20"
    )
    datas.map(x=>{
      val list = x.split(",")
      val id = Try(list(0)).getOrElse(0)
      val name = Try(list(1)).getOrElse("")
      val age = Try(list(2).toInt).getOrElse(-1)
      val area = Try(list(3)).getOrElse("")
      (id,name,age,area)
    }).filter(_._3 > 0).foreach(println)

    //todo 方式3
    //当执行语句执行正常时Left,否则Right,他们位置可以互换
    def devide(a:Int,b:Int)={
      try{
        Left(a/b)
      }catch{
        case e:Exception => Right(b,e)
      }
    }
  }
}

第八章 隐式转换

8.1 隐式方法

--隐式方法[悄悄的将一个类型转成另一个类型]
  (1)语法: implicit def 方法名(变量名: 待转换类型):目标类型 = {..}
  (2)隐式转换方法的调用时机:
     a.当前类型与目标类型不一致的时候,会自动的使用隐式转换
     b.对象使用了不属于自己的方法,也会自动的使用隐式转换
  "隐式转换时根据类型查找的,并不是根据名称"

//案例1:定义隐式转换方法,
  //1.定义两个没1分钱关系的两个类
  class Person(val name:String,val age:Int)
  class Student(val name:String,val age:Int)
  //2.定义隐式转换方法,使入参对象获得出参对象的属性
  implicit  def studentToPerson(student:Student):Person={
    println("------------")
    new Person(student.name,student.age)
  }
  //3.可以直接创建对象使用其属性和方法了
  val p:Person = new Student("zhangsan",20)
  println(p.name)
  println(p.age)

//案例2:为Int类增加方法
//自定义类,定义两个方法
class MyRichInt(val self: Int) {
  def myMax(i: Int): Int = {
    if (self < i) i else self
  }

  def myMin(i: Int): Int = {
    if (self < i) self else i
  }
}
object TestImplicitFunction {
  //定义隐式转换函数,使Int类型获得自定义类的方法
  implicit def convert(arg: Int): MyRichInt = {
    new MyRichInt(arg)
  }

  def main(args: Array[String]): Unit = {
    println(2.myMax(6))
  }
}

8.2 隐式参数

--1.隐式参数语法
  implicit val 参数名:参数类型 =--2.隐式参数在方法中调用
  在定义方法的时候必须指定哪个参数后续会传递隐式参数的值
  def 方法名(参数名:参数类型,..)(implicit 参数名:参数类型)
  
--3.隐式参数说明1)同一个作用域中,相同"类型"的隐式值只能有一个
 (2)编译器按照"隐式参数的类型去寻找对应类型的隐式值",与隐式值的名称无关。
 (3)隐式参数优先于默认参数

//案例1:隐式参数在方法中调用
def add(a:Int)(implicit b:Int) = a+b
def main(args: Array[String]): Unit = {
    println(add(10)(5))
  }

//案例2:测试编译器调用隐式参数时是按照类型调用的
object TestImplicitParameter {
  //1.定义隐式转换参数  
  implicit val str: String = "hello world!"
  //2.隐式转换方法
  def hello(implicit arg: String="good bey world!"): Unit = {
    println(arg)
  }
  //当调用hello时,按照类型进行查找隐式转换,因此调用的是隐式转换参数
  def main(args: Array[String]): Unit = {
    hello  //hello world!
  }
}

8.3 隐式类

--1.语法
  implicit class 类名(待转换类型){...}

--2.说明
  (1) 隐式转换类必须要有构造器,"构造器的参数有且仅有一个",参数类型就是待转换类型
  (2) 隐式转换类不能处于最顶层,必须放在object/class/package object(3) 实质上还是将一个对象转为另一个对象

object $03_ImplicitClass {
  //定义隐式转换类 构造器内传入待转换类型[Int -> Person]
  implicit class Person(x:Int){
    val name:String = "jason"
    val age:Int = 20+x
  }

  def main(args: Array[String]): Unit = {
    //直接通过Int类型对象调用隐式类中的属性和方法
    val a: Int = 10
    println(10.name)
    println(10.age)
  }
}

8.4 隐式机制解析

--隐式机制1)首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)--一般情况下2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的"全部伴生对象"以及该类型所在包的"包对象"
//(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,
object TestTransform extends PersonTrait {
    def main(args: Array[String]): Unit = {
        //(1)首先会在当前代码作用域下查找隐式实体
        val teacher = new Teacher()
        teacher.eat()
        teacher.say()
    }
    class Teacher {
        def eat(): Unit = {
            println("eat...")
        }
    }
}

trait PersonTrait {}

object PersonTrait {
    // 隐式类 : 类型1 => 类型2
    implicit class Person5(user:Teacher) {

        def say(): Unit = {
            println("say...")
        }
    }
}

第九章 泛型

9.1 泛型方法

//泛型方法 语法:
	def 方法名[T,U,..](参数名:T,..) :U = {...}

//案例
object GenericMethod {
  def main(args: Array[String]): Unit = {
    val arr =Array[Int](10,20,30,5)
    println(getMax[Int](10,20))
  }
  //函数类型、入参出参类型时都可以使用泛型
  def getMax[T](x:T,y:T):T = {
    x
  }
}

9.2 泛型类

//泛型类 语法:
class 类名[泛型1,泛型2,..](val 属性名:泛型,..){
  def 方法名(参数:泛型):泛型 = {...}
}

//案例
object GenericClass{
  //定义泛型类
  class Person[T,U](var name:T,var age:U){
      def getName() = this.name
      def setName(name:T) = {
        this.name=name
      }
      def getAge() = this.age
      def setAge(age:U) = this.age = age
    }
  def main(args: Array[String]): Unit = {
    //调用泛型类
    val person = new Person[String,Int]("zhangsan",20)
    println(person.getName())
  }
}

9.3 上下限

//1.上限: [代表T只能是指定的类型或者指定类型的子类]
	Class PersonList[T <: Person]  
//2.下限: [代表T只能是指定的类型或者指定类型的父类] --很难做限制,Any是任何类的父类
	Class PersonList[T >: Person]  

object GenericUpperLower {
  class Parent
  class Sub1 extends Parent
  class Sub2 extends Sub1
  class Sub3 extends Sub2

  def main(args: Array[String]): Unit = {
   //必须是sub1或sub1的子类
   // m1(new Parent)
      
	//必须是sub1或sub1的子类,Any也可以
    val sub3:Any = "spark"
    m2(sub3)
  }
  //上限
  def m1[T<:Sub1](x:T) = {
    println(x)
  }
  //下限
  def m2[T>:Sub2](x:T) = {
    println(x)
  }
}

9.4 非变、协变与逆变

//1.语法:
  --非变 Son是Father的子类,则MyList[Father]与MyList[Son]“无父子关系”
class MyList[T]
  --协变 Son是Father的子类,则MyList[Son] 也作为MyList[Father]的“子类”
class MyList[+T]
  --逆变 Son是Father的子类,则MyList[Son]作为MyList[Father]的“父类”
class MyList[-T]

//2.案例:
object GenericChange {

  class Person
  class Student extends Person

  //非变
  class AA[T]
  //协变
  class BB[+T]
  //逆变
  class CC[-T]

  def main(args: Array[String]): Unit = {

    var aa = new AA[Person]
    var bb = new AA[Student]
    //aa = bb

    //协变就是继承泛型的父子关系
    var cc = new BB[Person]
    var dd = new BB[Student]

    cc = dd
    //逆变就是颠倒泛型的父子关系
    var ee = new CC[Person]
    var ff = new CC[Student]

    ff = ee
      
    println(ff)
  }
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值