new project
确定项目所属组及名称,以及存放目录
创建scala目录
将scala目录设置为Sources Root
添加scala支持
package chapter01 /* * object : 关键字,申明一个单例对象(伴生对象) */ object HelloWorld { /* main 方法:从外部可以直接调用执行的方法 语法: def 方法名称(参数名称:参数类型):返回值类型={方法体} */ def main(s: Array[String]): Unit = { println("hello world!") System.out.println("hello world! This is from java.") } }
scala的注释,同java
单行注释
//
多行注释
/* */
文档注释
/** * */
基本语法
var 变量名[:变量类型]=初始值 var i:int = 1 //变量的设置方式 val 变量名[:变量类型]=初始值 val i:int = 1 //常量的设置方式
注意事项
Scala 对各种变量、方法、函数等命名时使用的字符序列称为标识符
基本语法
字符串,通过+号连接
object Test { def main(args:Array[String]):Unit={ val num:Int = 10 var name:String = "N" print("这儿有"+num+"个"+name) } }
printf用法:字符串,通过%传值
object Test { def main(args:Array[String]):Unit={ val num:Int = 10 var name:String = "N" printf("这儿有%d个%s",num,name) } }
字符串模板(插值字符串):通过$获取变量值
object Test { def main(args: Array[String]): Unit = { val num: Int = 10 var name: String = "N" println(s"这儿有${num}个${name}") val num1: Double = 2.1234 println(f"这儿有${num1}%2.2f个${name}") // 格式化模板字符串 println(raw"这儿有${num1}%2.2f个${name}") // 所有字符串全部原样输出 println( s""" |select * |from boy t |where t.age < ${num1} |and t.name=\'${name}\' |""".stripMargin) //通过三引号,能够忽略换行 } }
键盘输入基本语法
StdIn.readLine()、StdIn.readShort()、StdIn.readDouble() ......
import scala.io.StdIn object Test { def main(args: Array[String]): Unit = { print("姓名:") val name = StdIn.readLine() print("年齡:") val age = StdIn.readLine() println(s"${name}的年齡是:${age}!") } }
文件读取基本语法
import java.io.{File, PrintWriter} import scala.io.Source object Test { def main(args: Array[String]): Unit = { // 从文件中读取数据 Source.fromFile("src/main/resources/test.txt").foreach(print) // 将数据写入文件 // scala可以使用java的语法 val writer = new PrintWriter(new File("src/main/resources/output.txt")) writer.write("hello world") writer.close() } }
图例
说明
Byte、Short、Int、Long
数据类型 | 描述 |
---|---|
Byte [1] | 8 位有符号补码整数。数值区间为 -128 到 127 |
Short [2] | 16 位有符号补码整数。数值区间为 -32768 到 32767 |
Int [4] | 32 位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long [8] | 64 位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 = 2 的(64-1)次方-1 |
各进度数值相加,在不溢出的情况下,可以使用强制转换
Unit类型、Null类型、Nothing类型
数据类型 | 描述 |
---|---|
Unit | 表示无值,和其他语言中 void 等同。用作不返回任何结果的方法的结果 类型。Unit 只有一个实例值,写成()。 |
Null | null , Null 类型只有一个实例值 null |
Nothing | Nothing 类型在 Scala 的类层级最低端;它是任何其他类型的子类型。 当一个函数,我们确定没有正常的返回值,可以用 Nothing 来指定返回类 型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数 或者变量(兼容性) |
自动类型转换
当 Scala 程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这 个就是自动类型转换(隐式转换)。
强制类型转换
自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上 强制转函数,但可能造成精度降低或溢出
object Test { def main(args: Array[String]): Unit = { val n1:Int = (2.5+2.9).toInt print(n1) } }
object Test { def main(args: Array[String]): Unit = { for (i <- 0 to 9){ // 范围遍历 ,前后闭合的范围 //类似于 for(i <- 0.to(9)) println(i+". hello world!") } } } object Test { def main(args: Array[String]): Unit = { for (i <- 0 until 9){ //不包含边界的范围遍历 ,前闭合后开的范围 //类似于 for(i <- 0.to(9)) println(i+". hello world!") } } } object Test { def main(args: Array[String]): Unit = { for (i <- 0 to 9 if i != 5){ /* 循环守卫 循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为 true 则进入循环体内部,为 false 则跳过,类似于 continue。 * */ println(i+". hello world!") } } } object Test { def main(args: Array[String]): Unit = { for (i <- 0 to 9 by 2){ /* 循环步长 * */ println(i+". hello world!") } for(i <- 9 to 1 by -1){ println(i) } for(i <- 1 to 9 reverse){ println(i) } } } object Test { def main(args: Array[String]): Unit = { for(i <- 1 to 3; j <- 1 to 5){ /* 嵌套for循环 **/ println("i="+i+";"+"j="+j) } } } object Test { def main(args: Array[String]): Unit = { /* 循环返回值 说明:将遍历过程中处理的结果返回到一个新 Vector 集合中,使用 yield 关键字。 * */ val result: immutable.IndexedSeq[Int] = for (i <- 1 to 10) yield { i } print(result) } }
java中的写法
public class Test { public static void main(String[] args){ // 通过抛出异常的方式,实现break的效果 try { for(int i=0; i<10; i++){ if(i==3){ //break; throw new RuntimeException(); } System.out.println(i); } } catch (RuntimeException e) { //e.printStackTrace(); } System.out.println("这是循环外面的代码"); } }
scala中的写法
import scala.util.control.Breaks object Test { def main(args: Array[String]): Unit = { // 通过抛出异常的方式,实现break的效果 try { for (i <- 1 to 10) { if (i == 5) { throw new RuntimeException() } println(i) } } catch { case e: Exception => // 什么都不做,只是退出循环 } println("这是在循环外") // 使用scala中的Breaks类的break方方,实现异常的抛出和捕获 Breaks.breakable( for (i: Int <- 0 to 10) { if (i == 5) { Breaks.break() } println(i) } ) println("这是在循环外") } }
基本语法
return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
object Test { def main(args:Array[String]):Unit={ def f(s:String):String = { //return s //等同于如下 s } println(f("hello")) } }
如果函数体只有一行代码,可以省略花括号
object Test { def main(args:Array[String]):Unit={ def f(s:String):String = s //省略了花括号{} println(f("hello")) } }
返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
object Test { def main(args:Array[String]):Unit={ def f(s:String) = s // :和返回值类型一起省略 println(f("hello")) } }
如果有 return,则不能省略返回值类型,必须指定
object Test { def main(args:Array[String]):Unit={ def f(s:String):String = { // 必须指定返回值类型 return s } println(f("hello")) } }
如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
Scala 如果期望是无返回值类型,可以省略等号
object Test { def main(args:Array[String]):Unit={ // 将无返回值的函数称为过程 def f(s:String){ // 省略等号 println(S) } f("hello") } }
如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
object Test { def main(args:Array[String]):Unit={ def f = (s:String)=>{println(s)} //省略了def后的表达式赋值给f def fc(f:String => Unit):Unit = { // 此中的String => Unit 整体表示为一个函数的声明,其中String作为参数类型,Unit作为返回值类型 f("hello") } fc(f) // 等同于 fc((s:String)=>{println(s)}) } }
语法
没有名字的函数就是匿名函数。
(x:Int) => { 函数体 }
传递匿名函数至简原则(当传递的函数有一个参数)
参数的类型可以省略,会根据形参进行自动的推导
object Test { def main(args: Array[String]): Unit = { def fc(f: String => Unit): Unit = { f("hello") } fc((s)=>{println(s)}) } }
类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参 数超过 1 的永远不能省略圆括号
object Test { def main(args: Array[String]): Unit = { def fc(f: String => Unit): Unit = { f("hello") } fc(s=>{println(s)}) } }
匿名函数如果只有一行,则大括号也可以省略
object Test { def main(args: Array[String]): Unit = { def fc(f: String => Unit): Unit = { f("hello") } // 匿名函数如果只有一行,则大括号也可以省略 fc(s=>println(s)) } }
如果参数只出现一次,则参数省略且后面参数可以用_代替
object Test { def main(args: Array[String]): Unit = { def fc(f: String => Unit): Unit = { f("hello") } //如果参数只出现一次,则参数省略且后面参数可以用_代替 fc(println(_)) } }
传递2个参数
object Test { def main(args: Array[String]): Unit = { def fc(f: (Int, Int) => Int): Int = { f(1, 2) } val a = (x:Int,y:Int) => x+y val b = (x:Int,y:Int) => x-y println(fc(a)) println(fc(b)) } }
函数作为值进行传递
object Test { def main(args: Array[String]): Unit = { def f(s: String): String = { s } // 函数本体作为值传递 val fc = f _ // 在被调用函数f后面加上 _ ,相当于把函数f当成一个整体,传递给常量fc val fa: String => String = f // 如果明确变量类型,不使用下划线也可以将函数作为整体传递给变量 println(fc) // chapter05.Test$$$Lambda$1/189568618@1fbc7afb println(fc("hello fc")) // hello fc println(fa) // chapter05.Test$$$Lambda$2/495053715@45c8e616 println(fa("hello fa")) // hello fa } }
object Test { def main(args: Array[String]): Unit = { // 定义二元计算函数 def f(op: (Int, Int) => Int, a: Int, b: Int): Int = { op(a, b) } def fc(a: Int, b: Int): Int = { return a + b } // 普通的函数作为参数,进行传递 println(f(fc, 1, 2)) } }
object Test { def main(args: Array[String]): Unit = { // 将函数作为返回值 def f(): String => Unit = { def fc(a: String): Unit = { println(a + " fc") } fc } println(f()("hello")) } }
函数传值的应用
object Test { def main(args: Array[String]): Unit = { def f(array:Array[Int],op:Int => Int):Array[Int]={ for(elem <- array) yield op(elem) } def fc(a:Int):Int={ a*2 } // val newArray = f(Array(1,2,3,4),fc:Int=>Int) val newArray = f(Array(1,2,3,4),fc _) // 等同于上面的写法 println(newArray.mkString(" ")) // 传入匿名函数 println(f(Array(1,2,3,4),_*2).mkString(",")) } }
定义
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和它所处的环境,称为闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表
定义
一个函数/方法在函数/方法体内又调用了本身,称为递归调用
object Test { def main(args: Array[String]): Unit = { // 常规方式完成递归 def f(n: Int): Int = { if (n == 0) return 1 return f(n - 1) * n } println(f(5)) // 尾递归 def fc(n: Int): Int = { def fcc(n: Int, curResult: Int): Int = { if (n == 0) return curResult fcc(n - 1, curResult * n) } fcc(n, 1) } println(fc(5)) } }
值调用:把计算后的值传递过去
object Test { def main(args: Array[String]): Unit = { // 传值参数,传递一个值 def f(a:Int):Unit={ println(" a= "+a) } f(1) } }
名调用:把代码传递过去
object Test { def main(args: Array[String]): Unit = { // 传名参数 def f(a: => Int): Unit = { // =>Int,表示代码块,需要返回的值为Int类型 println("获取返回值 a= " + a) } f(1) println("*"*20) f({ println("这是要执行的代码快") return 2 }) } }
定义
当函数返回值被申明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会被执行,这种函数称之为惰性函数
object Test { def main(args: Array[String]): Unit = { lazy val result: Int = sum(1, 2) println("执行步骤1 ") println("执行步骤2:result=" + result) } def sum(a: Int, b: Int): Int = { println("执行步骤3") a + b } }
基本语法
package 包名
scala包的作用
scala的命名
命名规则
只能包含数字、字母、下划线、小圆点,但不能用数字开头,也不允许使用关键字
命令规范
一般是小写字母+小圆点
com.公司名.项目名.业务模块名
包说明
scala有两种包管理风格:
和java相同,每个源文件一个包(包名和源文件所在路径不要求一致,属于逻辑上的关系),包名用"."进行分隔以表示包的层级关系
通过嵌套的风格表示层级关系
package com{ package nuochengze{ package scala{ } } }
包对象
在scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以被直接访问
定义
package object com{ val shareValue = "shate" def shareMethod()={} }
说明
导包说明
在顶部使用 import 导入,在这个文件中的所有类都可以使用
在顶部使用 import 导入,在这个文件中的所有类都可以使用
通配符导入:import java.util._
给类起名:import java.util.{ArrayList=>JL}
导入相同包的多个类:import java.util.{HashSet, ArrayList}
屏蔽类:import java.util.{ArrayList =>_,_}
Scala 中的三个默认导入分别是
import java.lang._ import scala._ import scala.Predef._
基本语法
[修饰符] class 类名{ 类体 }
定义
属性是类的一个组成部分
基本语法
[修饰符] var|val 属性名称 [:类型] = 属性值
定义
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
语法
def 方法名(参数列表) [:返回值类型] = { 方法体 }
语法
val|var 对象名[:类型] = new 类型()
说明
scala构造对象需要调用构造方法,并且可以有任意多个构造方法
scala类的构造器包括:主构造器和辅助构造器
语法
class 类名(形参列表){ // 主构造器,即为对象本身 // 类体 def this(形参列表){ // 辅助构造器,可以有多个 } }
继承语法
class 子类名 extends 父类名 {类体}
多态
一个接口的多种实现方式
scala中属性和方法都是动态绑定,而java中只有方法为动态绑定
语法
继承&重写
匿名子类
可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类
scala是完全面向对象的语言,为了能够和java语言交互(java中有静态概念,scala中没有静态概念),产生了一种特殊的对象来模拟类对象,该对象称为单例对象。当单例对象与类名一致时,称该单例对象为这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
单例对象语法
object Person{ ... }
其他
说明
特质基本语法
说明
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,在使用时采用extends关键字,如果有多个特质或存在父类,需要采用with关键字连接
基本语法
没有父类
class 类名 extends 特质1 with 特质2 with 特质3...
有父类
class 类名 extends 父类 with 特质1 with 特质2 with 特质3...
其他说明
类和特质的关系:使用继承的关系
当一个类去继承特质时,第一个连接词是extends,后面是with
如果一个类在同时继承特质和父类时,应该把父类写在extends后
一个类可以混入(mixin)多个特质
所有的java接口都可以当作scala特质使用
动态混入:创建对象时混入trait,而无需使类混入该trait;如果混入的trait中有未实现的方法,则需要实现
object Test { def main(args: Array[String]): Unit = { val f = new testClass() with testTrait{ override var value1: String = "这是重写的特质中的属性" override def f1(): Unit = { println("这是重写的特质中的方法") } } f.f1() println(f.value1) } trait testTrait{ // 定义抽象属性和非抽象属性 var value1:String var value2:String= _ // 定义抽象方法和非抽象方法 def f1():Unit def f2():Unit={ println("f2——:特质中的非抽象方法") } } class testClass(){ println("testClass——:测试类") } }
特质叠加
当由一个类混入多个trait时,如果混入的特质具有相同的方法(方法名,参数列表,返回值均相同),则会出现继承冲突问题
倒三角问题
一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且 两个 trait 之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法
菱形问题
一个类(Sub)混入的两个 trait(TraitA,TraitB)中具有相同的具体方法,且 两个 trait 继承自相同的 trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala 采用了特质叠加的策略
// 所谓的特质叠加,就是将混入的多个 trait 中的冲突方法叠加起来 object Test { def main(args: Array[String]): Unit = { println(new traitSub().f()) } trait traitC { def f(): String = { "traitC" } } trait traitA extends traitC { override def f(): String = { "traitA-" + super.f() } } trait traitB extends traitC { override def f(): String = "traitB-" + super.f() } class traitSub extends traitA with traitB { override def f(): String = "Test value: " + super.f() } }
特质叠加执行顺序
当一个类混入多个特质的时候,scala 会对所有的特质及其父特质按照一定的顺序进行 排序,而此案例中的 super.f()调用的实际上是排好序后的下一个特质中的f() 方法
如果想要调用某个指定的混入特质中的方法,可以增加约束:super[]
例如:super[traitA].f()
特质自身类型
说明
自身类型可实现依赖注入的功能
class A(val x:String){ } trait B{ def f(a:A):Unit = { println(s"B--->>>println ${a.x}") } } trait C{ _: B => // 自身类型可实现依赖注入的功能 def fc(a:A):Unit={ println(s"C--->>>println ${a.x}") f(a) } } object Test extends C with B{ def main(args: Array[String]): Unit = { fc(new A("hello")) } }
特质总结
说明
使用type关键字可以定义新的数据的数据类型名称,本质上就是类型的一个别名
object Test { def main(args: Array[String]): Unit = { type S=String var v:S="abc" def test():S="xyz" } }
不可变集合继承图
可变集合继承图
第一种方式定义数组
val arr = new Array[Int](10)
object Test { def main(args: Array[String]): Unit = { val arr = new Array[Int](10) for( a <- 0 until arr.length){ arr(a) = a } println(arr.mkString(" ")) } }
第二种方式定义数组
val arr = Array(1,2,3)
object Test { def main(args: Array[String]): Unit = { val arr = new Array[Int](10) for( a <- 0 until arr.length){ arr(a) = a } // 1.用下标遍历数组 for( a <- arr.indices){ // def indices: Range = 0 until length print(arr(a)+" ") } println() // 2.用增强for循环遍历 for(a <- arr){ print(a+" ") } println() // 3.用迭代器遍历 val iter = arr.iterator while(iter.hasNext){ print(iter.next+" ") } println() // 4.调用foreach方法,遍历 arr.foreach((x:Int) => print(x+" ")) println() // 5.以特定的连接条件,输出整体的元素 println(arr.mkString(",")) println("*"*20) // 6 往数组后面添加元素(其实是新生成一个数组替代) val newArr = arr.:+(66) println("arr: "+ arr.mkString(" ")) println("newArr: "+ newArr.mkString(" ")) // 7 往数组前面添加元素 println("往数组前面添加元素"+"*"*20) val newArr2 = arr.+:(77) println("arr: "+ arr.mkString(" ")) println("newArr2: "+ newArr2.mkString(" ")) val newArr3 = newArr2 :+ 66 // 简写,运算符的本质是方法 println("newArr3: "+ newArr3.mkString(" ")) val newArr4 = 55 +: 66 +: newArr2 //简写,运算符的本质是方法 println("newArr4: "+ newArr4.mkString(" ")) } }
定义变长数组
var arr = ArrayBuffer[Any](1,2,3)
import scala.collection.mutable.ArrayBuffer object Test { def main(args: Array[String]): Unit = { // 1.创建数组 val arr1 = ArrayBuffer[Int]() var arr2 = ArrayBuffer(1,2,3) println(arr1) println(arr2) // 2.往可变数组尾部添加元素 arr1.+=(1) arr1 += 2 arr1.append(3) println(arr1) // 3.往可变数组头部添加元素 arr1.+=:(0) -1 +=: arr1 arr1.prepend(-2) println(arr1) // 4.在任意位置添加数组 arr1.insert(0,6,6) println(arr1) // 5.insertAll,prependAll,appendAll,在任意、最前面、最后面添加集合 // 6 删除元素 arr1.remove(1,3) // 按位置和数量删除 println(arr1) arr1 -= 3 // 按元素删除 println(arr1) } }
说明
import scala.collection.mutable.ArrayBuffer object Test { def main(args: Array[String]): Unit = { // 从可变数组转换为不可变数组 var arr1 = ArrayBuffer(1,2,3) val newArr1 = arr1.toArray println(arr1) println(newArr1) // 从不可遍数组传唤为可变数组 val arr2 = Array(1,2,3) var newArr2 = arr2.toBuffer println(arr2) println(newArr2) } }
多维数组定义
val arrDim2 = Array.ofDim[Int](3,3)
object Test { def main(args: Array[String]): Unit = { val arrDim2 = Array.ofDim[Int](3,3) arrDim2(1)(2) = 2 arrDim2(2)(2) = 1 for(i <- 0 until arrDim2.length;j <- 0 until arrDim2(i).length){ println(arrDim2(i)(j)) } println("*"*20) for(i <- arrDim2.indices;j <- arrDim2(i).indices){ print(arrDim2(i)(j) + "\t") if(j == arrDim2(i).size-1){println()} } println("*"*20) arrDim2.foreach(_.foreach(println)) } }
object Test { def main(args: Array[String]): Unit = { // 1.创建一个List val list1 = List(1,2,3,4) // List通过伴生对象创建 println(list1) // 2.访问和遍历数组 println(list1(1)) list1.foreach(println) println("*"*20) // 3.添加元素 val list2 = list1.+:(10) // 在头部添加元素 val list3 = 10 +: list1 println(list2) println(list3) println("*"*20) val list4 = list1.:+(11) // 在尾部添加元素 val list5 = list1 :+ 11 println(list4) println(list5) val list6 = list1.::(51) // 通过 :: 方法在头部添加元素 println(list6) val list7 = Nil.::(66) // 通过Nil快速创建列表 println(list7) val list8 = 1 :: 2 :: 3 :: Nil // 由右到左创建列表 println(list8) // 3 合并两个列表 println("*"*20) val list9 = list4 :: list5 println(list4) println(list5) println(list9) println("*"*20) val list10 = list4 ::: list5 // 通过 ::: 方法在头部添加元素,且将列表扁平化处理,得到一个新的列表 val list11 = list4 ++ list5 // ::: 等同于 ++ println(list10) println(list11) } }
import scala.collection.mutable.ListBuffer object Test { def main(args: Array[String]): Unit = { val list1 = new ListBuffer[Int]() val list2 = ListBuffer(1,2,3) println(list1) println(list2) } }
说明
默认情况下,scala使用的是不可变集合,想使用可变集合,需要引用scala.collection.mutable.Set包
object Test { def main(args: Array[String]): Unit = { // 1.创建set val set1 = Set(1, 2, 3, 4, 4, 5, 2, 3) println(set1) // 2.添加元素 val set2 = set1.+(20) // 集合在内是无序的,且唯一 val set3 = set1 + 20 println(set2) println(set3) // 3.合并集合 val set4 = Set(1,2,4,4,5,6,7,8) val set5 = set3 ++ set4 println(set5) // 4 删除元素 val set6 = set5 - 1 println(set6) } }
import scala.collection.mutable object Test { def main(args: Array[String]): Unit = { // 1.创建set val set1 = mutable.Set(1, 2, 3, 4, 4, 5, 2, 3) println(set1) println("*"*20) // 2.添加元素 val set2 = set1 + 6 println(set1) println(set2) println("*"*20) set1 += 33 println(set1) println("*"*20) set1.add(44) println(set1) // 3 删除元素 println("*"*20) set1 -= 2 println(set1) println("*"*20) set1.remove(4) println(set1) // 4.合并集合 println("*"*20) val set3 = mutable.Set(12,2,3,9) println(set1) println(set3) val set4 = set1 ++ set3 println(set1) println(set3) println(set4) println("*"*20) println(set1) println(set3) set3 ++= set1 // set1集合不便,set3集合值改变 println(set1) println(set3) } }
说明
scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射
object Test { def main(args: Array[String]): Unit = { // 1.创建Map val map1 = Map("a" -> 12, "b" -> 22,"c" -> 11) println(map1) // 2.遍历元素 map1.foreach(println) println("*"*20) map1.foreach((kv:(String,Int)) => println(kv)) // 等效于 map1.foreach(println) // 3 取map中所有的key 或者value println("*"*20) for(key <- map1.keys){ println(s"${key}--->>>${map1.get(key)}") } // 4.访问某一个key的value值 println("*"*20) println(map1("a")) println(map1.get("a").get) println(map1.get("d")) println(map1.getOrElse("d",0)) // 防止无值导致的Some(x)=None,而无get方法,从而造成异常 }}
import scala.collection.mutable object Test { def main(args: Array[String]): Unit = { // 1.创建Map val map1 = mutable.Map("a" -> 12, "b" -> 22,"c" -> 11) println(map1) // 2.添加元素 map1.put("d",10) map1.put("e",222) map1.+=(("f",666)) // 作用同 map1.put("d",10) println(map1) // 3.删除元素 println("*"*20) map1.remove("c") println(map1.getOrElse("c",0)) println(map1) map1.-=("d") println(map1) // 4.修改元素 println("*"*20) map1.update("m",222) // 当map中无对应值时,表现为插入操作 println(map1) map1.update("a",999) // 当map中有对应值时,表现为更新操作 println(map1) // 5.合并Map val map2 = Map("x" -> 12, "y" -> 22,"z" -> 11) map1 ++= map2 println(map1) } }
定义
元组可以理解为一个容器,可以存放各种相同或不同类型的数据。即元组,将多个无关的数据封装为一个整体
说明
import scala.collection.mutable object Test { def main(args: Array[String]): Unit = { // 1.创建元组 val tuple1: (String, Int, Char, Boolean) = ("hello", 100, 'a', true) println(tuple1) // 2.访问数据 println(tuple1._1) // 通过._num的方式访问 println(tuple1.productElement(0)) // 通过productElement方式访问,索引以0开头 // 3.遍历元组 println("*" * 20) for (elem <- tuple1.productIterator) { println(elem) } // 4.嵌套元组 println("*" * 20) val multuple = (10, 22, (12, "a"), "hello") println(multuple._3._1) // 5. 对偶 println("*" * 20) val map = Map("a"->1, "b"->2, "c"->3) val map1 = Map(("a",1), ("b",2), ("c",3)) map.foreach(tuple=>{println(tuple._1 + "=" + tuple._2)}) } }
object Test { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4) val set = Set(1,2,3) // 1.获取线性集合长度 println(list.length) println(list.size) // 2.获取非线性集合长度 println(set.size) // 3.遍历 list.foreach(print) println() set.foreach(print) println() for(elem <- list){ print(elem) } println() for(elem <- set){ print(elem) } println() // 4.迭代器 for(elem <- list.iterator){ print(elem) } println("*"*20) // 5.生成字符串 println(list) println(set) println(list.mkString(",")) println("*"*20) // 6.是否包含某个元素 println(list.contains(23)) println(set.contains(2)) } }
object Test { def main(args: Array[String]): Unit = { val list1 = List(1, 2, 3, 4) val list2 = List(3,4,243,53) // 1.获取集合的头 println(list1.head) // 2.获取集合的尾(这里的尾,指的是除了头的部分) println(list1.tail) // List(2, 3, 4) // 3.获取集合的最后一个数据 println(list1.last) // 4.获取集合初始数据(不包含最后一个数据的剩余部分) println(list1.init) // 5 反转集合 println(list1.reverse) // 6 取前(后)n个元素 println(list1.take(3)) // 取前3个元素 println(list1.takeRight(2)) // 取后2个元素 // 7.去掉前(后)n个元素 println(list1.drop(2)) // 去掉前2个元素 println(list1.dropRight(3)) // 去掉后3个元素 println("*"*20) // 8.并集 val union_ = list1.union(list2) println("union_: "+ union_) println(list1:::list2) // union等于将集合加在一起 println(list1 ++ list2) println(Set(1,2,3,4).union(Set(3,4,5))) // 如果是set做并集操作,会去重 // 9.交集 println("交集"+"*"*20) println(list1.intersect(list2)) // 10.差集 println("差集"+"*"*20) println(list1.diff(list2)) println(list2.diff(list1)) // 11.拉链 println("拉链"+"*"*20) println(list1.zip(list2)) // list1中的元素占_v1 println(list2.zip(list1)) // list2中的元素占_v1 // 12.滑窗 println("滑窗"+"*"*20) for(elem <- list1.sliding(3)){ // 确定滑窗的范围 println(elem) } println("-"*20) for(elem <- list1.sliding(3,2)){ // 确定滑窗的范围+滑动的距离 println(elem) } println("-"*20) for(elem <- list1.sliding(3,3)){ // 确定滑窗的范围等于滑动的距离时,又称为滚动窗口 println(elem) } } }
object Test { def main(args: Array[String]): Unit = { val list1 = List(13, 6, 3, 4) val list2 = List(("a",22),("b",2),("c",4),("d",7),("e",9)) // 1.求和 println(list1.sum) // 2.求乘积 println(list1.product) // 3.求最大值 println(list1.max) // 3.1 maxBy println("maxBy"+"*"*20) println(list2.max) // 默认按照._v1进行取值 println(list2.maxBy((tuple:(String,Int)) => tuple._2 )) // 通过传递匿名函数,按._v2进行取值 println(list2.maxBy(_._2)) // 简写 // 4.求最小值 println("minBy"+"*"*20) println(list1.min) println(list2.min) println(list2.minBy(_._2)) // 简写 // 5.排序 // 5.1 sorted 从小到大排序 println("sorted"+"*"*20) println(list1.sorted) // 5.2 sorted.reverse 从大到小排序 println("sorted.reverse"+"*"*20) println(list1.sorted.reverse) // 5.3 sorted.implicit 传入隐式参数 println("sorted.implicit"+"*"*20) println(list1.sorted(Ordering[Int].reverse)) // 5.4 sorted.implicit 传入隐式参数 println("sorted.implicit"+"*"*20) println(list1.sorted(Ordering[Int].reverse)) // 5.5 sortBy println("sortBy"+"*"*20) println(list2.sortBy(_._2)) println(list2.sortBy(_._2)(Ordering[Int].reverse)) //逆序排列 // 5.6 sortWith println("sortWith"+"*"*20) println(list1.sortWith((a:Int,b:Int) => {a<b} )) // 从小到大排列 println(list1.sortWith(_<_)) // 从小到大排列 简化写法 println(list1.sortWith(_>_)) // 从大到小排列 简化写法 } }
object Test { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9) // 1.过滤:遍历一个集合,并从中获取满足制定条件的元素组成一个新的集合 println("filter" + "*" * 20) println(list.filter((elem: Int) => { elem % 2 == 0 })) // 获取列表中的偶数 println(list.filter(_ % 2 == 1)) // 获取奇数 // 2.map: 转换/映射(map) 将集合中的每一个元素映射到某一个函数 println("map" + "*" * 20) println(list.map((elem: Int) => { elem * 2 })) // 集合中的元素值*2 println(list.map(_ * 2)) // 集合中的元素值*2 简化 // 3.扁平化 flatten println("扁平化" + "*" * 20) val nestedList = List(List(1, 2, 43), List(2, 2, 4), List(2, 1), List(2, 4, 6, 8, 3)) println(nestedList) println(nestedList(0) ::: nestedList(1) ::: nestedList(2) ::: nestedList(3)) // 扩展性较差 println(nestedList.flatten) // 4.扁平映射 println("扁平映射" + "*" * 20) val list2: List[String] = List("hello world", "hello java", "hello scala") println(list2) val splitList2: List[Array[String]] = list2.map(elem => elem.split(" ")) //分词 println(splitList2.flatten) // 打散 println(list2.flatMap(elem => elem.split(" "))) //flatMap结合了以上的分词和打散 // 5.分组 println("分组" + "*" * 20) println(list.groupBy(_ % 2)) /* groupBy对列表list进行分组,其中groupBy(f)内函数f的返回值作为map的key, 待归类的词作为map的value // Map(1 -> List(1, 3, 5, 7, 9), 0 -> List(2, 4, 6, 8)) */ println(list.groupBy((data:Int)=>{if(data%2==0) "偶数" else "奇数"})) } }
object Test { def main(args: Array[String]): Unit = { val list = List(1,2,3,4) // 1.reduce 简化(归约):通过指定的逻辑将集合中的数据进行聚合,从而减少数据,获取最终的结果 println("归约"+"*"*20) println(list.reduce(_+_)) // 功能同sum println(list.reduceLeft(_+_)) // reduce的底层是reduceLeft println(list.reduceRight(_+_)) // 2.reduceLeft和reduceRight的区别 val list2 = List(1,2,3,4) println(list2.reduceLeft(_-_)) // 结果为-8 println(list2.reduceRight(_-_)) // 结果为-2 ,源码为递归操作:(1-(2-(3-4))) } }
object Test { def main(args: Array[String]): Unit = { // 1.Fold val list = List(1,2,3,4) println(list.fold(10)(_+_)) // 执行步骤:10 +1+2+3+4 println(list.foldLeft(10)(_-_)) //fold的底层是foldLeft 执行步骤:10 -1-2-3-4 println(list.foldRight(10)(_-_)) // 执行步骤:1-(2-(3-(4-10))) /* fold方法使用了函数柯里化,存在两个参数列表 第一个参数列表为: 零值(初始值) 第二个参数列表为: 简化规则 * */ } }
案例1:Map的简单累加
import scala.collection.mutable object Test { def main(args: Array[String]): Unit = { val map1 = mutable.Map("a" ->1 ,"b" -> 2, "c" -> 3) val map2 = mutable.Map("a" ->4 ,"b" -> 5, "c" -> 6,"d" -> 2) val map3 = map2.foldLeft(map1){ (map,kv) =>{ val k = kv._1 val v = kv._2 map(k) = map.getOrElse(k,0)+v map } } println(map3) } }
案例2:普通的worldCount案例
object Test { def main(args: Array[String]): Unit = { val stringList = List( "hello", "hello world", "hello scala", "hello spark from scala", "hello flink from scala" ) // 1.对字符串进行切分,得到一个打散所有单词的列表 val wordList: List[String] = stringList.map(_.split(" ")).flatten println(wordList) // 2.相同的单词进行分组 val wordGroup: Map[String, List[String]] = wordList.groupBy((word) => { word }) println(wordGroup) // 3.对分组之后的List取长度,得到每个单词的个数 val wordCount: Map[String, Int] = wordGroup.map((kv) => { (kv._1, kv._2.length) }) println(wordGroup) // 4.将map转换为list,并排序(用sortBy排序)取前3 println(wordCount.toList) println("用sortBy排序"+"*"*20) val wordCountSortedBy: List[(String, Int)] = wordCount.toList.sortBy((tuple: (String, Int)) => { tuple._2 })(Ordering[Int].reverse) println(wordCountSortedBy) println(wordCountSortedBy.take(3)) // 5.将map转换为list,并排序(用sortWith排序)取前3 println("用sortWith排序"+"*"*20) val wordCountSortedWith: List[(String, Int)] = wordCount.toList.sortWith(_._2 > _._2) println(wordCountSortedWith) println(wordCountSortedWith.take(3)) } }
案例3:复杂的wordCount案例
object Test { def main(args: Array[String]): Unit = { val tupleList: List[(String, Int)] = List( ("hello", 3), ("hello world", 2), ("hello scala", 4), ("hello spark from scala", 1), ("hello flink from scala", 5) ) // 思路1:直接展开 println("思路1:直接展开" + "*" * 20) val newStringList: List[String] = tupleList.map((kv) => { (kv._1.trim + " ") * kv._2 }) println(newStringList) val result1: List[(String, Int)] = newStringList.flatMap( _.split(" ") ).groupBy((word) => { word }).map((kv) => { (kv._1, kv._2.length) }).toList.sortBy( (tuple) => { tuple._2 } )(Ordering[Int].reverse).take(3) println(result1) // 思路2:直接基于预统计的结果进行转换 println("思路2:直接基于预统计的结果进行转换" + "*" * 20) println( tupleList .flatMap((tuple) => { val strings = tuple._1.split(" ") strings.map((k) => { (k, tuple._2) }) }).groupBy((tuple) => { tuple._1 }) .map((tupleList) => { val v2Sum: Int = tupleList._2.map((tuple) => { tuple._2 }).sum (tupleList._1, v2Sum) }) .toList .sortBy((tuple) => { tuple._2 })(Ordering[Int].reverse) .take(3) ) } }
说明
scala提供了队列(Queue)的数据结构,其特点是先进先出。
进队和出队的方式分别为enqueue和dequeue
可变队列
import scala.collection.mutable object Test { def main(args: Array[String]): Unit = { val mutableQueue = new mutable.Queue[String]() // 1.进入队列 mutableQueue.enqueue("a","b","c") println(mutableQueue) // 2.出队列 println("result: "+ mutableQueue.dequeue()) // 返回值为出队列的值 println(mutableQueue) } }
不可变队列
import scala.collection.immutable object Test { def main(args: Array[String]): Unit = { val immutableQueue = immutable.Queue[String]("a","b","c") // 不可变队列使用伴生对象创建值 // 1.进入队列 println("进入队列"+"*"*20) println(immutableQueue) val newImmutableQueue = immutableQueue.enqueue("d") // 其本身的值不会改变,需要用新变量取接受 println(immutableQueue) println(newImmutableQueue) // 2.出队列 println("出队列"+"*"*20) val result = immutableQueue.dequeue //返回值类型 (String, Queue[String]) println(immutableQueue) // 其本身的值不会改变 println(result) } }
说明
scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的计算
import scala.collection.immutable import scala.collection.parallel.immutable.ParSeq object Test { def main(args: Array[String]): Unit = { val result1: immutable.IndexedSeq[Long] = (1 to 10).map( (x) => { Thread.currentThread.getId } ) println(result1) // Vector(1, 1, 1, 1, 1, 1, 1, 1, 1, 1) val result2: ParSeq[Long] = (1 to 10).par.map( (x) => { Thread.currentThread.getId } ) println(result2) // ParVector(10, 10, 12, 14, 14, 11, 11, 13, 13, 13) } }
定义
op match{ case a => a case b => b case _ => "Nothing" }
=>
后面的代码块,直到下一个case语句之前的代码都是作为一个整体执行,可以用{}括起来,也可以省略object Test { def main(args: Array[String]): Unit = { val a = A("aaa",111) val result = a match{ case A("aaa", 111) => a.str+" "+a.value case _ => "Nothing" } println(result) } }
说明
如果想要表达式匹配某个范围的数据,就需要在模式匹配中增加条件守卫
object Test { def main(args: Array[String]): Unit = { val a = 1; a match { case a if a>=0 => println("a大于0") case a if a<0 => println("a小于0") case _ => println("Nothing") } } }
Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等
object Test { def main(args: Array[String]): Unit = { def des(x:Any) : Unit = { x match{ case x: Int => println("Int " + x) case x: String => println("String " + x) case x: List[Int] => println("List " + x) case x: Array[Int] => println("Array " + x.mkString(",")) case x => println("Nothing "+ x) } } des(List(1,2,3,4)) des(List("hello","world")) // 针对列表类型,虽然定义了具体的泛型,但是存在类型擦除,即并不关心是Int,还是String,都会按默认类型输出 des(Array("hello","world")) des(Array(1,2,3)) //数组例外,可保留泛型 // 输出结果 // List List(1, 2, 3, 4) // List List(hello, world) // Nothing [Ljava.lang.String;@7dc36524 // Array 1,2,3 } }
scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素 为 0 的数组
object Test { def main(args: Array[String]): Unit = { for (arr <- Array( Array(0), Array(1, 0), Array(0, 0, 1), Array(2, 1, 2), Array("hello", 90) )) { arr match { case Array(0) => println("匹配 Array(0) 这个数组") case Array(x, y) => println("匹配两个元素的数组 " +x+" " + y) // 匹配有两个元素的数组,然后将将元素值赋给对应的 x,y case Array(0, _*) => println("匹配以 0 开头数组") case _ => println("Something else") } } /* 匹配 Array(0) 这个数组 匹配两个元素的数组 1 0 匹配以 0 开头数组 Something else 匹配两个元素的数组 hello 90 * */ } }
匹配对象
object Test { def main(args: Array[String]): Unit = { val a:A = A("h",1) a match{ case A("h",1) => println("Yes") case _ => println("No") } } } class A(val v1:String,val v2:Int) object A { def apply(v1: String, v2: Int): A = { new A(v1, v2) } def unapply(a:A):Option[(String,Int)] = { if(a == null){ None }else{ Some(a.v1,a.v2) } } }
val a:A = A("h",1)
这条语句在执行时,实际调用的A伴生对象中的apply方法,因此不用new关键字也能构造出相对应的对象
当A("h",1)
在case后时,会默认调用unapply方法(对象提取器),unapply方法将A对象的属性提取出来
若只提取对象的一个属性,则提取器为 unapply(obj:Obj):Option[T]
若提取对象的多个属性,则提取器为 unapply(obj:Obj):Option[(T1,T2,T3…)]
若提取对象的多个属性,则提取器为 unapply(obj:Obj):Option[(T1,T2,T3…)]
样例类
语法
case class A(val v1:String)
object Test { def main(args: Array[String]): Unit = { val a:A = A("h",1) a match{ case A("h",1) => println("Yes") case _ => println("No") } } } case class A(val v1:String,val v2:Int)
语法
object Test{ def main(args:Array[String]):Unit={ try{ var n = 10/0 }catch{ case ex:ArithmeticException => {println("发生算术异常")} case ex:Exception => {println("发生了异常")} }finally{ println("终处理") } } }
Scala没有编译异常的概念,异常都是在运行的时候捕获处理
按编程贵方,需要把越具体的异常写在前面
finally 子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作
用throw关键字,抛出异常
def test():Nothing ={ throw new Exception("异常") }
在scala中,可以使用throws注解来声明异常
object Test{ def main(args:Array[String]):Unit={ f() } @throws(classOf[NumberFormatException]) def f() = { "abc".toInt } }
说明
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译
说明
隐式转换可以在不需要修改任何代码的情况下,扩展某个类的功能
隐式函数
class MyRichInt(val arg:Int){ def valueMax(i:Int):Int = { if(arg<i) i else arg } } object Test{ def main(args:Array[String]):Unit={ implicit def convert(arg:Int):MyRichInt = { new MyRichInt(arg) } /* 当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐式转换。也称之为自动转换 */ println(2.valueMax(15)) } }
隐式类
object Test{ implicit class MyRichInt(val arg:Int){ def valueMax(i:Int):Int = { if(arg<i) i else arg } } def main(args:Array[String]):Unit={ println(2.valueMax(15)) } }
说明
普通方法或者函数中的参数可以通过implicit关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值
object Test{ def main(args:Array[String]):Unit={ implicit val name:String = "world" implicit val i:Int = 10 def sayHello()(implicit arg:String):Unit ={ println("hello "+arg) } def sayInt()(implicit arg:Int):Unit={ println("hi "+arg) } // sayHello的简单写法 def sayHello_():Unit={ println("hello "+ implicitly[String]) } sayHello() sayHello_() sayInt() } }
语法
class MyList[+T]{//协变} class MyList[-T]{//逆变} class MyList[T]{ //不变}
语法
def f[A : B](a: A) = println(a) //等同于 def f[A](a:A)(implicit arg:B[A])=println(a)
说明
上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]] 获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误