第一章 Scala基础篇

一、Scala基础语法

===

(一) 变量、类型、操作符

1.变量申明

●Java中:

//数据类型 变量名 = 值;
final int b = 2;
b = 23; //错误!!! final修饰的变量不可变
int a = 1;
a = 22;

●在Scala中:

//val/var 变量名:变量类型 = 变量值
val a:Int = 1
a =2 //错误!!!! val修饰的变量不可以被重新赋值,类似于Java中的final
var b:Int = 1
b = 2

●说明

var声明的变量可以重新赋值

val声明的变量不可以重新赋值,或称之为不可变变量/只读变量。相当于java里用final修饰的变量

●注意:

Scala中变量声明类型可以省略,解析器会根据值进行推断

Scala中语句最后不需要添加分号

val和var声明变量时都必须初始化

为了减少可变性引起的bug,应该尽可能地使用不可变变量val。(推荐使用val)

如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号:

val `val` = 123    //(符号为键盘上Esc下面的键)

●Type关键字

Scala 里可以通过type 关键字来声明类型。

type 相当于声明一个类型别名:

// 把String 类型用S 代替
type S = String
val name: S = "bigdata"
println (name)

●懒值

应用场景

​ 当val被声明为lazy时,初始化将被推迟,只有当这个变量真正被使用到的时候,变量的赋值代码才会真正的执行

​ lazy适用于初始化开销较大的场景

●代码演示

package com.flyingAfish.baseTest

object VariableDemo {
  def main(args: Array[String]): Unit = {
    val name:String = "fizz"
    //name = "jack ma" //错误,val修饰的变量不能被重新赋值
    var money:Int = 8888
    money = 565//var修饰的变量可以重新赋值

    val age = 18 //:Int 类型可以省略,编译器会自动推断age的类型
    //age = "fizz" //错误,因为scala是强类型

    type str = String //type关键字可以给类型起别名
    val animal:str = "fish"
  
    //当val被声明为lazy时,初始化将被推迟,只有当这个变量真正被使用到的时候,变量的赋值代码才会真正的执行
    //lazy适用于初始化开销较大的场景
    val msg1 = init()
    println("我是按顺序执行的")
    println(msg1)
    println("===============")
    lazy val msg2 = init()
    println("我先于init方法执行")
    println(msg2)
  }

  //定义一个方法,输出一句话并返回一个字符串
  def init() ={
    println("init方法执行了")
    "msg"
  }
}

/*	init方法执行了
    我是按顺序执行的
    msg
    ===============
    我先于init方法执行
    init方法执行了
    msg
*/

2.字符串

​ scala提供多种定义字符串的方式,我们可以根据需要来选择最方便的定义方式。

●双引号

val/var 变量名 = "字符串"

●三引号

​ 如果有大段的文本需要保存,就可以使用三引号来定义字符串。例如:保存一大段的SQL语句。三个引号中间的所有字符串都将作为字符串的值。

val/var 变量名 = """字符串1字符串2"""

●使用插值表达式

​ scala中,可以使用插值表达式来定义字符串,有效避免大量字符串的拼接。

val/var 变量名 = s"${变量/表达式}字符串"
val/var 变量名 = s"""${变量/表达式}字符串"""

●代码演示

package com.flyingAfish.baseTest

object StringDemo {
  def main(args: Array[String]): Unit = {
    val name = "fizz"
    val sql =
      """
        |select *			// | 竖条为编译器自动添加
        |from table
        |where name = fizz
      """.stripMargin		//.stripMargin 编译器自动添加
    val sql2 =
      s"""					//s 为编译器自动添加
        |select *
        |from table
        |where name = ${name}
      """.stripMargin		

    println(name)
    println(sql)
    println(sql2)
  }
}
/*
	fizz

    select *
    from table
    where name = fizz

    select *
    from table
    where name = fizz  
*/

3.数据类型

●数值类型

Scala和Java一样,有多种数值类型Byte、Char、Short、Int、Long、Float、Double类型和1个Boolean类型。

Boolean true 或者 false
Byte 8位, 有符号
Short 16位, 有符号
Int 32位, 有符号
Long 64位, 有符号
Char 16位, 无符号
Float 32位, 单精度浮点数
Double 64位, 双精度浮点数
String 其实就是由Char数组组成

●继承体系

![img](file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml19704\wps2.png)

●Any

在scala中,所有的类,包括值类型,都最终继承自一个统一的根类型Any,Any类是根节点

Any中定义了isInstanceOf、asInstanceOf方法,以及哈希方法等。AnyVal和AnyRef都扩展自Any类。

​ isInstanceOf:是否继承自某类

​ asInstanceOf:强制类型转换

●AnyVal-所有值类型的基类,所有的值都是类类型都是AnyVal的子类

- scala.Double

- scala.Float

- scala.Long

- scala.Int

- scala.Char

- scala.Short

- scala.Byte

上面是数字类型。

还包括scala.Unit 和 scala.Boolean 是非数字类型。

●AnyRef-是所有引用类型的基类。

​ 除了值类型,所有其他类型都继承自AnyRef

●Null

​ 是所有引用类型的子类型,Null类只有一个实例对象,null,类似于Java中的null引用。null可以赋值给任意引用类型,但是不能赋值给值类型

●Nothing

是所有类型的子类型。Nothing类型没有实例。它对于泛型结构是有用处的,举例:

空列表Nil的类型是List[Nothing],它是List[T]的子类型,T可以是任何类。

Nothing可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。

●Unit

用来标识过程,也就是没有明确返回值的函数。

由此可见,Unit类似于Java里的void。Unit只有一个对象实例(),这个实例也没有实质的意义。

●注意

1.Scala并不刻意区分基本类型 和 引用类型,所以这些类型都是对象,可以调用相对应的方法。

2.每一种数值类型都有对应的Rich类型,如RichInt、RichChar等,为基本类型提供了更多的有用操作。(重要

3.String直接使用的是java.lang.String类,另外在scala.collection.immutable.StringOps中还定义了更多的操作。在需要时String能隐式转换为StringOps,因此不需要任何额外的操作,String就可以使用这些方法。

4.操作符

Scala中的+ - * / %等操作符的作用与Java一样,位操作符 & | ^ >> <<也一样。

●注意:

1.Scala中的操作符实际上是方法

2.Scala中没有++、--操作符,需要通过+=、-=来实现同样的效果(因为++ -- 前置后置容易混淆)

3.+ - * / %是方法,那么就可以进行操作符重载,完成特殊的运算(也就是自己在类中定义+ - * / %方法表示特殊的运算)

●高级:

1)中置操作符,

//A操作符B 等同于 A.操作符(B) 
val a = 1
val b = 2
var c = a + b     
var d = a.+(b)	//等同与上式

2)后置操作符,

​ A操作符 等同于 A.操作符,如果操作符定义的时候不带()则调用时不能加括号

math.random

3)前置操作符,

​ +、-、!、~等操作符 A等同于 A.unary_操作符。

var b = true
b = !b
b = b.unary_!	//等同与上式

4)赋值操作符,

​ A操作符=B 等同于 A=A操作符B

a += 1
a = a + 1	//等同与上式

●代码演示

package com.flyingAfish.baseTest

object OperatorDemo {
  def main(args: Array[String]): Unit = {
    var a = 1
    var b = 2
    val result = a + b
    println(result)//3
    val result2 = a.+(b) //在Scala中运算符其实是方法
    println(result2)//3
    //a++ //错误,在Scala中为了避免混淆,不支持++ --
    a += 1 //a = a + 1
    println(a)//2
  }

(二)循环判断

1.块表达式

定义变量时可以使用 {} 包含一系列表达式,其中{}块的最后一个表达式的值就是整个块表达式的值。

●代码演示

package com.flyingAfish.baseTest

object E_BlockDemo {
  def main(args: Array[String]): Unit = {
    var a = 1
    var b  =2
    var c = {
      a = a + b
      b = a + b
      var i = a + b
      i 		//注意:{}块表达式的最后一行是整个表达式的值
    }
    println(c)//8
  }
}

2.条件表达式

Scala条件表达式的语法和Java一样,只是更加简洁,且Scala中if else表达式是有返回值的

●注意:

Scala中没有三元表达式

如果if或者else返回的类型不一样,就返回Any类型

对于没有返回值的,使用Unit,写做(),相当于java中的void

●代码演示

package com.flyingAfish.baseTest

object ConditionDemo {
  def main(args: Array[String]): Unit = {
    val sex1 = "male"
    val sex2 = "female"

    //if表达式有返回值
    val result1:String = if(sex1 == "male"){
      "男"
    }else{
      "女"
    }
    println(result1)

    val result2:String = if(sex1 == "male") "男" else "女"
    println(result2)

    //如果返回值类型不一致,返回Any
    val result3:Any = if(sex2 == "male"){
      "男"
    }else{
      0
    }
    println(result3)

    //()表示没有返回值,即Unit,相当于Java中的void
    val result4:Unit = if(sex2 == "male"){
      println("男")
    }else{
      println("女")
    }
    println(result4)//()
  }
}

3.循环表达式

在scala中,可以使用for循环和while循环,但一般推荐使用for表达式,因为for表达式语法更简洁

●简单for循环:

​ for (变量 <- 表达式/数组/集合) {循环体}

for(i <- 1 to 10){println(i)} //循环打印1~10

●嵌套for循环

​ for (变量1 <- 表达式/数组/集合; 变量2 <- 表达式/数组/集合) {循环体}

for(i <- 1 to 9; j <- 1 to 9){
     if(i >= j ) print(s"${j} * ${i} = ${j*i}")
     if(j == 9) println()
}

●守卫

​ for表达式中,可以添加if判断语句,这个if判断就称之为守卫。我们可以使用守卫让for表达式更简洁。

​ for(i <- 表达式/数组/集合 if 表达式) {循环体}

for(i <- 1 to 10 if i % 3 == 0) println(i) //3,6,9

●for推导式(有返回值)

​ 在for循环体中,可以使用yield表达式构建出一个集合,我们把使用yield的for表达式称之为推导式

​ 即可以使用for推导式生成一个新的集合(一组数据)

//该for表达式会构建出一个集合
val res = for(i <- 1 to 10) yield i * 10 //10,20,30,40...

●注意:

​ while、for语句本身没有值,即整个while语句的结果是Unit类型的()

var n = 1;
val result:unit = while(n <= 10){
  	n += 1
}
println(result)
println(n)

●代码演示

package com.flyingAfish.baseTest

object LoopDemo {
  def main(args: Array[String]): Unit = {
    //1 to 10 ===> [1,10]
    val res1 = 1 to 10 //Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    //1 until 10 ===> [1,10)
    val res2 = 1 until 10 //Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
    //1 to 10 ===> [1,10],步长为2
    val res3 = 1 to 10 by 2 //Range(1, 3, 5, 7, 9)
    println(res1)
    println(res2)
    println(res3)

    //1.简单for循环
    for(i <- 1 to 10) println(i)

    println("===========分割线==========")
    //2.嵌套for循环--传统写法
    for(i <- 1 to 9){
      for(j <- 1 to 9){
        if(i >= j){
          print(s"${j} * ${i} = ${j * i} \t")
        }
      }
      println()
    }
    println("===========分割线==========")
    //2.嵌套for循环--Scala骚气写法
    for(i <- 1 to 9;j <- 1 to 9){
      if(i >= j) print(s"${j} * ${i} = ${j * i} \t")
      if(j == 9) println()
    }
    println("===========分割线==========")
    //3.守卫
    //需求打印1~10中3的倍数
    for(i <- 1 to 10){
      if(i % 3 == 0) println(i)
    }
    println("===========分割线==========")
    for(i <- 1 to 10 if(i % 3 == 0)) println(i)

    println("===========分割线==========")
    //4.for推导式
    //需求把1~10中的每一个数扩大10倍形成一个新的集合
    //使用yield关键字可以生成一个新的集合
    val col = for(i <- 1 to 10) yield i * 10
    println(col)//Vector(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)

    println("===========分割线==========")
    //5.注意:while循环没有返回值
    var n =1
    val result = while (n <= 100){
      n += 1
      n
   }
    println(n)//101
    println(result)//()
  }
}

(三)方法和函数

1.方法

def 方法名(参数名1: 参数类型1, 参数名2: 参数类型2) : 返回类型 = {方法体}

img

●语法细节

\1. 方法的返回值类型和return可以不写,编译器可以自动推断出来

\2. 对于递归方法必须指定返回类型

\3. 如果方法没有返回值,返回Unit类型(类似于void,也可以不写)

\4. 返回值类型有多种情况则返回Any

\5. 带有默认值参数的方法,调用时,可以给定新值,也可以使用默认值

\6. 可以通过参数名来指定传递给哪一个参数,这样传递参数时就可以不按照顺序传递

\7. 方法没有参数,调用时可以省略(),如果定义时()省略,调用时则必须省略

\8. 可变参数使用 变量名: 类型* (类似Java的...

●代码演示

package com.flyingAfish.baseTest

object MethodDemo {
  def main(args: Array[String]): Unit = {
    val result: Int = factorial(3)
    println(result)//6
    println(noReturnValue())//()
    //val resutl2: Int = haveDefaultParam(b=2)//102
    //val resutl3: Int = haveDefaultParam(a=1,b=2)//3
    //val resutl4: Int = haveDefaultParam(1,2)//3
    val resutl5: Int = haveDefaultParam(b=2,a=1)//3
    //println(resutl2)
    //println(resutl3)
    //println(resutl4)
    println(resutl5)

    noParam

    val sum: Int = multiParam(1,2,3,4,5)//15
    println(sum)
  }
  
  
  //def 方法名(参数名1:参数类型,参数名1:参数类型):返回值类型 = {方法体}
  //1.方法的返回值类型和return可以不写,编译器可以自动推断出来
  def omitReturnValue() ={
     1
  }

  //2.对于递归方法,必须指定返回类型
  //需求:求n的阶乘 ==> 10的阶乘=1*2*3...*10 == 10 * 9 * 8 .....
  def factorial(n:Int):Int = {
    if (n == 1){
      1
    }else{
      n * factorial(n-1)
    }
  }

  //3.如果方法没有返回值,返回Unit类型(类似于void,也可以不写)
  def noReturnValue():Unit={
    println("m3")
  }
  //4.返回值类型有多种情况则返回Any或者不写
  val sex = "male"
  def multiReturnValue():Any={
    if (sex == "male"){
      "男"
    }else{
      0
    }
  }
  //5.带有默认值参数的方法,调用时,可以给定新值,也可以使用默认值
  //6.可以通过参数名来指定传递给哪一个参数,这样传递参数时就可以不按照顺序传递
  def haveDefaultParam(a:Int=100,b:Int):Int={
    println("a="+a)
    println("b="+b)
    a + b
  }
  //7.方法没有参数,调用时可以省略(),如果定义时()省略,调用时则必须省略
  def noParam={
    println("m7")
  }
  //8.可变参使用 变量名:类型* (类似Java的...)
  //定义了一个方法可以传递多个int值,并返回他们的和
  def multiParam(args:Int*):Int={
    var sum = 0
    for (i <- args){
      sum +=i
    }
    sum
  }
}

2.函数

●完整语法:

​ val函数名称 :(参数类型)=>函数返回值类型 = (参数名称:参数类型)=>函数体

val sum:(Int,Int)=>Int = (a:Int,b:Int)=>{a + b}

●简写语法:

​ val函数名称 = (参数名称:参数类型) => 函数体

val max = (a:Int,b:Int)=>{
    if(a > b) a
    else b
}

●符号解释

​ = 表示将右边的函数 赋给 左边的变量

​ => 左面表示输入参数名称和类型,右边表示函数的实现和返回值类型

3.方法和函数的区别

●方法:

​ 和之前学习Java时理解的方法一样,是封装了完成某些功能的代码块,所属于某一个类或对象

●函数:

​ 在Scala中,函数是头等公民,函数是一个对象,那么既然是对象的话,函数就可以赋值给变量 或者 当作参数被传递,还可以 使用函数打点调用方法

​ Scala中函数继承自FuctionN,带有一个参数的函数的类型是function1,带有两个是function2,以此类推

●证明函数是对象

●函数是对象,可以打点调方法

package com.flyingAfish.baseTest

object FunctionDemo {
  def main(args: Array[String]): Unit = {
    //val函数名称 = (参数名称:参数类型) => 函数体
    val add1 = (a:Int) => {a }
    val add2 = (a:Int,b:Int) => {a + b}
    val add3: (Int, Int, Int) => Int = (a:Int, b:Int, c:Int) => {a + b + c}
    println(add1)//<function1>
    println(add2)//<function2>
    println(add3)//<function3>
    
    //结论:函数是对象,有N个参数打印的时候就是functionN
    val str: String = add1.toString()//函数是对象,所以可以调用方法
    println(str)//<function1>
    
    //函数一般用法
    println(add2(3,2))//5
  }
}

●函数可以赋值给变量并将可以当作参数进行传递

​ 定义一个函数,接收两个参数,返回两个数的和

​ 定义一个方法,接收两个参数和一个函数,并在方法体中调用函数,将两个参数传递给函数

package com.flyingAfish.baseTest

object FunctionDemo {
  def main(args: Array[String]): Unit = {
    //函数可以赋值给变量并将可以当作参数进行传递
    //定义一个函数,接收两个参数,返回两个数的和
    val add = (a:Int,b:Int) => a + b //证明了函数是对象,且函数可以赋值给变量
    val result1: Int = operator(1,2,add) //证明了函数是对象,且函数可以当作参数进行传递
    println(result1)//3

    //函数的好处:
    //可以将我们要进行的操作进行传递!!!
    //也就是函数式编程的核心思想:行为参数化!!!
    val result2: Int = operator(1,2, (a,b)=> a*b)
    println(result2)

    println("=======================")
    //再来体会一下:行为参数化!!!
    //val list = List(1,2,3,4,5)
    val list = 1 to 5
    //val f = (i:Int) => println(i)
    //list.foreach((i:Int) => println(i))
    //list.foreach(i => println(i))
    //list.foreach(println(_))
    list.foreach(println)
  }

  //定义一个方法,接收两个参数和一个函数,并在方法体中调用函数,将两个参数传递给函数
  def operator(a:Int,b:Int,fun:(Int,Int)=>Int) ={
    fun(a,b)
  }
}

●方法可以转换成函数

​ 证明方法无返回值也可以调用

​ Java里面方法无返回值不能调用

package com.flyingAfish.baseTest

object I_FunctionDemo4 {
    def main(args: Array[String]): Unit = {
        println(fizz()) //() 证明函数五返回值也可以条用,打印的是空括号()
        println(fizz _) //下划线 _可以将方法变成函数 //<function0>
    }
    
    def fizz() = {
        
    }
}

4.总结

●定义方法:

​ def 方法名(参数名1: 参数类型1, 参数名2: 参数类型2) : 返回类型 = {方法体}

●定义方法简写法:

​ def 方法名(参数名1: 参数类型1, 参数名2: 参数类型2) = {方法体}

​ def 方法名 = {方法体}

●定义函数完整语法:

​ val函数名称 :(参数类型)=>函数返回值类型 = (参数名称:参数类型)=>函数体

●定义函数简写语法:

​ val函数名名称 = (参数名称:参数类型) => 函数体

![img](file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml19704\wps4.png)

●注意:

​ 严格的来说,函数与方法是不同的东西。定义方式上也有很多的区别

​ 但是在Scala中,函数本身是对象,方法可以通过下划线_转换为函数。

结论:在Scala中

​ 方法可以转换为函数,函数本质上就是对象

​ 函数式编程的核心思想、灵魂所在:行为参数化!

二、Scala常用数据结构/集合

(一)Scala集合分类和继承体系

1.分类

●集合分类-按照数据结构特点分

​ Scala的集合都扩展自Iterable特质(先理解为接口)

​ 有三大类:Seq序列(List)、Set、Map映射

●集合分类-按照可变和不可变分(注意:这里的可变和不可变指的是集合的内容和长度,和之前的var/val有区别)

​ 大部分的集合Scala都同时提供了可变和不可变的版本。

​ 开发时建议Scala优先采用不可变集合(默认即为不可变),满足不了需求是再使用可变集合

●可变集合和不可变集合相应的包为:

​ 不可变集合:scala.collection.immutable (默认)

​ 可变集合: scala.collection.mutable

●注意

val和可变不可变

​ var和val指的是:变量能否被重新赋值

​ 集合可不可变指的是:集合长度或内容可不可变

对于数组:

​ 不可变数组Array:长度不可变,元素可变(定长数组)

​ 可变数组ArrayBuffer:长度和里面的元素都可变(变长数组)(注意:Java里面数组长度不可变)

对于其他集合:

​ 不可变集合immutable:长度和内容都不可变,如果调用添加或者删除方法,会产生新的集合,原集合不变

​ 可变集合mutable:长度和内容都可变

总结:

​ 开中优先使用不可变,如果满足不了需求再使用可变

2.继承体系

●不可变集合(immutable )继承层次:

![img](file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml19704\wps5.png)

●可变集合(mutable)继承层次:

![img](file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml19704\wps6.png)

(二)数组

●不可变/定长数组:

val/var 变量名= new Array[T](数组长度)//scala.collection.immutable包下,不需要导包,默认就是
val/var 变量名 = Array(元素1, 元素2, 元素3...)

●可变/变长数组:

val/var 变量名 = ArrayBuffer[T]() //需要手动导入import scala.collection.mutable.ArrayBuffer包
val/var 变量名 = ArrayBuffer(元素1, 元素2, 元素3...)

●数组操作

指定分隔符 mkString

将数组转换成数组缓冲 toBuffer(打印Buffer可以看到数组内容)

根据索引获取元素 ()

添加元素 +=

删除元素 -=

追加一个数组到变长数组 ++=

往指定角标插入元素 insert

删除指定角标的元素 remove

定长=>>变长 toBuffer

变长=>>定长 toArray

多维数组 Array.ofDimDouble

●遍历数组

1.可以for循环直接遍历数组

2.可以遍历下标再根据下标获取元素

3.回忆一下生成指定范围的序列

0 to n 生成[0,n]

0 until n 生成[0,n)

●数组其他常用方法

在Scala中,数组上的某些方法对数组进行相应的操作非常方便!

sum求和

max求最大值

min求最小值

sorted排序

reverse反转

●代码演示

package com.flyingAfish.baseTest
import scala.collection.mutable

object ArrayDemo {
  def main(args: Array[String]): Unit = {
    val arr: Array[Int] = Array(5,6,7,1,2,3,4,8,9)//不可变
    //不可变==>可变
    val arr2: mutable.Buffer[Int] = arr.toBuffer
    //可变==>不可变
    val arr3: Array[Int] = arr2.toArray

    //遍历
    for(i <- arr) println(i)
    println("====================")
    //arr.foreach((i:Int)=>println(i))
    //arr.foreach(i=>println(i))
    //arr.foreach(println(_))
    arr.foreach(println)//行为参数化
    println("====================")
    //逆序
    for(i <- arr.reverse) println(i)
    println("====================")
    //通过索引遍历
    for(i <- 0 until arr.length) println(arr(i))

    //统计
    println(arr.sum)
    println(arr.max)
    println(arr.min)
    println(arr.reverse.mkString(","))
    println(arr.sorted.mkString(","))//按照默认的排序规则排序-升序
    println(arr.sortBy((i:Int) => -i).mkString(",")) //降序
    println(arr.sortWith((x:Int,y:Int) => x > y).mkString(","))//降序

  }
}

(三)元组

​ 元组也是可以理解为一个容器,可以用来存放各种相同或不同类型的数据。例如:姓名,年龄,性别,出生年月。

元组的元素是不可变的

●创建元组

​ 使用括号来定义元组

val/var 元组 = (元素1, 元素2, 元素3....)
val animal = ("fish","cat","dog")
val world = ("fizz",34,animal,34.43)

​ 使用箭头来定义元组(元组只有两个元素)

val/var 元组 = 元素1->元素2 //对偶/二元组是最简单的元组(k,v)
val animal: (String, String) = ("fizz", "dgo")
val dog: (String, Int) = "god" -> 3

●获取元组中的值

//使用下划线加脚标 ,例如 元组名._1  元组名._2 元组名._3
val animal: (String, String) = ("fizz", "dgo")
val dog: (String, Int) = "dog" -> 3
val name: String = animal._1
val age: Int = dog._2

注意:元组中的元素脚标是从1开始的

●将对偶(二元组)组成的数组转换成映射(映射就相当于Java中的Map,后面会讲)

将对偶/二元组的集合转换成映射:

调用其toMap 方法

![img](file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml19704\wps7.png)

●遍历

​ 可以调用元组的productIterator方法获取迭代器对象进行遍历

●代码演示

package com.flyingAfish.baseTest

object TupleDemo {
  def main(args: Array[String]): Unit = {
    //使用括号来定义元组
    //val/var 元组 = (元素1, 元素2, 元素3....)
    val t1: (String, Double, Int) = ("hadoop",3.14,110) //元组里可以存放不同类型的元素
    //使用箭头来定义元组(元组只有两个元素)
    //val/var 元组 = 元素1->元素2 //对偶/二元组是最简单的元组(k,v)
    val t2: (String, Int) = "age"->18
    val t3 = ("age",18)
    println(t1)
    println(t2)
    println(t3)
    println(t1.getClass)
    println(t2.getClass)
    println(t3.getClass)

    //获取元素
    println(t1._1)
    println(t1._2)
    println(t1._3)

    println("============")
    //val iterator: Iterator[Any] = t1.productIterator
    for(i <- t1.productIterator) println(i)

    //将二元组组成的集合转成map
    val ts = Array(("jack",60),("tom",70),("rose",80))
    val map: Map[String, Int] = ts.toMap
    println(map)//Map(jack -> 60, tom -> 70, rose -> 80)
  }
}

(四)List

●高能预警

​ List操作的API方法和符号特别特别多,不用刻意去记,后续学习中会使用一些常见的,用的多了就掌握了!

●List介绍

​ 列表是scala中最重要的、也是最常用的数据结构。在scala中,也有两种列表,一种是不可变列表、另一种是可变列表

但都具备以下性质:

​ 可以保存重复的值

​ 有先后顺序

●不可变列表(默认)

​ import scala.collection.immutable._

​ 创建方式1.使用List(元素1, 元素2, 元素3, ...)来创建一个不可变列表

val/var 变量名 = List(元素1, 元素2, 元素3...)
val strings: List[String] = List("book","subject","car")
val book: List[Any] = List("English", 32, "chinese", 300)

​ 创建方式2.使用::方法创建一个不可变列表

val/var 变量名 = 元素1 :: 元素2 :: Nil 
val money: List[Int] = 34 :: 34 :: 344 :: Nil

注意:

​ 使用::拼接方式来创建列表,必须在最后添加一个Nil表示空列表

●可变列表

​ import scala.collection.mutable._

​ 创建方式1.使用ListBuffer元素类型创建空的可变列表

val/var 变量名 = ListBuffer[Int]()
val fizz: ListBuffer[Int] = ListBuffer[Int]()

​ 创建方式2.使用ListBuffer(元素1, 元素2, 元素3...)创建可变列表

val/var 变量名 = ListBuffer(元素1,元素2,元素3...)
val personName: ListBuffer[Any] = ListBuffer("kashke",34,"wangzida")

●head和tail

​ 在Scala中列表要么为Nil(Nil表示空列表)

​ 要么是一个head元素加上一个tail列表。

//head为列表第一个元素,tail为列表除第一个元素外的元素列表
val list1 = List(1,2,3,4,5)
println(list1.head)//1
println(list1.tail)//List(2, 3, 4, 5)
val list = List(1)
println(list)//List(1)
println(list.head)//1
println(list.tail)//List()

●::操作符

:: 操作符是将给定的头和尾创建一个新的列表,原列表不变  
:: 操作符是右结合的,如1 :: 5 :: 2 :: Nil相当于 1 :: (5 :: (2 :: Nil))   ==> List(1,5,2)

●可变列表操作

 获取/更改元素(使用括号访问(索引值))
 添加元素(+=)
 追加一个列表(++=)
 删除元素(-=)
 转换为List(toList)
 转换为Array(toArray)

●扩展:list其他操作符(了解)

::  (x: A): List[A]                            在列表的头部添加一个元素或列表
+:  (elem: A): List[A]                         在列表的头部添加一个元素
:+  (elem: A): List[A]                         在列表的尾部添加一个元素
++  [B](that: GenTraversableOnce[B]): List[B]  从列表的尾部添加另外一个列表
:::  (prefix: List[A]): List[A]                   在列表的头部添加另外一个列表

●扩展:等价操作(了解)

val left = List(1,2,3)
val right = List(4,5,6)
//以下操作等价
left ++ right      // List(1,2,3,4,5,6)
right.:::(left)     // List(1,2,3,4,5,6)
//以下操作等价
0 +: left    //List(0,1,2,3)
left.+:(0)   //List(0,1,2,3)
//以下操作等价
left :+ 4    //List(1,2,3,4)
left.:+(4)   //List(1,2,3,4)
//以下操作等价
0 :: left      //List(0,1,2,3)
left.::(0)     //List(0,1,2,3)

●代码演示

package com.flyingAfish.baseTest

object ListDemo {
  def main(args: Array[String]): Unit = {
    //●不可变列表
    //val/var 变量名 = List(元素1, 元素2, 元素3...)
    val list1 = List(1,2,3,4,5)
    //val/var 变量名 = 元素1 :: 元素2 :: Nil
    val list2 = 1::2::Nil //List(1,2)

    //●可变列表
    //val/var 变量名 = ListBuffer[Int]()
    import scala.collection.mutable.ListBuffer
    val list3 = ListBuffer[Int]()
    //val/var 变量名 = ListBuffer(元素1,元素2,元素3...)
    val list4 = ListBuffer(1,2,3,4)
    list3.append(1,2,3)

    println(list1)
    println(list2)
    println(list3)//ListBuffer(1, 2, 3)
    println(list4)
    //List(1, 2, 3, 4, 5)
    //List(1, 2)
    //ListBuffer(1, 2, 3)
    //ListBuffer(1, 2, 3, 4)

    println(list1.head)//1
    println(list1.tail)//List(2, 3, 4, 5)
    val list = List(1)
    println(list)//List(1)
    println(list.head)//1
    println(list.tail)//List()

    println("============")
    list3.remove(1)//根据索引删除
    println(list3)//ListBuffer(1, 3)
    list3 += 4
    list3 -= 1

    val list5 = list3.toList
    val list6 = list3.toArray

    val list7 = 0 +: list3
    println(list7)//ListBuffer(0, 3, 4)
    
    list7

    //遍历
    for(i <- list3) println(i)
    list3.foreach(println)
  }
}

(五)队列

●说明

​ 队列数据存取符合先进先出的策略

​ 有 scala.collection.mutable.Queue 和 scala.collection.immutable.Queue

​ 一般来说我们在开发中队列通常使用可变队列(特殊),其他都推荐使用不可变

●常见操作

​ enqueue入队/+=追加

​ dequeue出队

●代码演示

package com.flyingAfish.baseTest
import scala.collection.mutable

object DueueDemo {
  def main(args: Array[String]): Unit = {
    //队列在开发中一般使用可变队列
    val q = mutable.Queue[Int]()
    q.enqueue(1)
    q.enqueue(2)
    q.enqueue(3)
    q += 4
    q.+=(5)
    println(q)//Queue(1, 2, 3, 4 , 5)
    val i: Int = q.dequeue()
    println(i)//1
  }
}

(六)Set

●Set说明

Set代表一个没有重复元素的无序集合;即无法加入重复元素且不保证插入顺序的。

●不可变Set(默认)

import scala.collection.immutable._

​ 1.创建一个空的不可变集,语法格式:

val/var 变量名 = Set[类型]()
val movie = Set[String]()

​ 2.给定元素来创建一个不可变集,语法格式:

val/var 变量名 = Set(元素1, 元素2, 元素3...)
val fresh = Set("apple","pear","apricot")

●可变Set

import scala.collection.mutable._

​ 格式相同,导包不同

Set操作

方法 描述
def +(elem: A): Set[A] 为集合添加新元素,并创建一个新的集合,除非元素已存在
def -(elem: A): Set[A] 移除集合中的元素,并创建一个新的集合
def contains(elem: A): Boolean 如果元素在集合中存在,返回 true,否则返回 false。
def &(that: Set[A]): Set[A] 返回两个集合的交集
def &~(that: Set[A]): Set[A] 返回两个集合的差集
def ++(elems: A): Set[A] 合并两个集合

●代码演示

package com.flyingAfish.baseTest

object SetDemo {
  def main(args: Array[String]): Unit = {
    //●不可变Set(默认)
    //val/var 变量名 = Set(元素1, 元素2, 元素3...)
    val set1 = Set(1,2,3,4,5,6,7)

    //●可变Set
    //格式相同,导包不同
    //val/var 变量名 = Set[类型]()
    import scala.collection.mutable._
    val set2 = Set[Int]()
    set2.add(1)
    set2.add(2)
    set2.add(3)
    val set3 = set2 + 4
    val set4 = set2 - 1
    set2.remove(2)
    println(set2)//Set(1, 3)
    println(set3)//Set(1, 2, 3, 4)
    println(set4)//Set(2, 3)

    val set5 = set2 ++ set3 //并集
    println(set5)//Set(1, 2, 3, 4)

    val set6 = set3 & set2
    println(set6)//Set(1, 3)//交集

    val set7 = set3 &~ set2//差集
    println(set7)//Set(2, 4)

//遍历和其他集合一样
  }
}

(七)Map

●说明

​ 在Scala中,把哈希表这种数据结构叫做映射类比Java的map集合

●不可变Map

import scala.collection.immutable.Map

​ 格式一:使用箭头

val/var map = Map(键->值, 键->值, 键->值...)   // 推荐,可读性更好
val hero = Map("fish"->"潮汐海灵","zeus"->"宙斯","baiqi"->"白起")

​ 格式二:利用元组

val/var map = Map((键, 值), (键, 值), (键, 值), (键, 值)...)
val subject = Map(("math","数学"),("English","英语"),("physics","物理"))

●可变Map

import scala.collection.mutable.Map

​ 格式相同,导包不同

●获取值

map(键)
map.get(键)
map.getOrElse(键,默认值)//根据键取值,如果取到了则返回,没取到返回指定的默认值

●修改值

map(键)=值

●增加值

map.put(键,值)

●代码演示

package cn.fizz.collection

object MapDemo {
  def main(args: Array[String]): Unit = {
    //●不可变Map
    //●可变Map
    //格式相同,导包不同
    import scala.collection.mutable._
    //格式一:使用箭头val/var map = Map(键->值, 键->值, 键->值...)   // 推荐,可读性更好
    val map1 = Map("tom"->60,"rose"->70,"jack"->80,"zhaosi"->100)
    //格式二:利用元组val/var map = Map((键, 值), (键, 值), (键, 值), (键, 值)...)
    val map2 = Map(("tom",60),("rose",70),("jack",80),("zhaosi",100))
    map1.put("lily",90)
    map1.remove("tom")
    println(map1)
    println(map2)

    //根据key取值
    //Option是None和Some的父类
    //None表示空的,什么都没有
    //Some表示里面有一个元素,在使用get就可以取出来
    val op: Option[Int] = map1.get("zhaosi")
    println(op.get)//100

    //直接使用get方法不好,如果没有指定的key,继续操作可能会报错
    //val op2: Option[Int] = map1.get("zaosi")
    //println(op2.get)

    val v: Int = map1.getOrElse("zaosi",0)
    println(v)//0

    val v2: Int = map1.getOrElse("zhaosi",0)
    println(v2)//100

    println("==========================")
    //遍历
    //1.通过key
    for(k <- map1.keySet) println(map1(k))
    println("==========================")
    //2.直接遍历value
    for(v <- map1.values) println(v)
    println("==========================")
    //3.通过元组
    for((k,v) <- map1) println(k+":"+v)
    println("==========================")
    //4.函数式的遍历+模式匹配
    map1.foreach{
      case (k,v) => println(k+":"+v)
    }
  }
}

--------------------------------------------------------------------
-----------------------java版本-------------------------------------
--------------------------------------------------------------------

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapDemo {
    public static void main(String[] args){
        Map<String, Integer> map = new HashMap<>();
        map.put("k1",1);
        map.put("k2",2);
        map.put("k3",3);
        map.put("k4",4);

        Set<String> set = map.keySet();
        for (String k : set) {
            System.out.println(map.get(k));
        }
        Collection<Integer> values = map.values();
        for (Integer value : values) {
            System.out.println(value);
        }
        //开发的时候建议使用entrySet
        //因为entrySet拿出来的就是所有的kv,不需要在根据k去找v!!!
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry.getKey() + ":"+entry.getValue());
        }
    }
}

posted on 2019-08-25 10:40  -小鱼-  阅读(354)  评论(0编辑  收藏  举报

导航