8.1 Scala基础语法
8.1.1 变量和数据类型
-
变量(var)
-
常量(val)
-
Scala中的键盘输入输出
import scala.io.StdIn object TestInput { def main(args: Array[String]): Unit = { // scala 输入输出 println("please input your name") var name = StdIn.readLine() println("please input your age") var age = StdIn.readInt() println(s"name is ${name} age is ${age}") } }
-
字符串的输入输出
object TestCharType { def main(args: Array[String]): Unit = { var name:String = "zhangsan" var age:Int = 22 // 字符串连接 println(name + " "+age) // zhangsan 22 // 字符串输出 printf("name=%s age=%d\n",name,age) // name=zhangsan age=22 // 插值表达式 println(s"name is ${name},age is ${age+2}") // name is zhangsan,age is 24 val s = """ |select | name |, |age |from user |where name = "zhangsan" """.stripMargin println(s) } }
stripMargin 使用场景:有时候我们就是确实需要在scala创建多行字符串,但是每一行需要固定对齐。
-
数据类型
-
数值类型(AnyVal)
object TestDataType { def main(args: Array[String]): Unit = { // 整数类型 byte short int long 整数类型 默认 int var byte1:Byte =127 var short1:Short = 32767 var long1:Long = 10L; // 浮点类型 fload double 浮点类型默认为double // 字符类型 char var c: Char = 'C' // 布尔类型 var is_ok: Boolean = true // Unit类型 类似 void // Null 类型 只能用于引用类型 var food = new Food() food = null // Nothing 类型 可以作为没有正常返回值的方法的返回类型 def test():Nothing={ throw new Exception } // test() // 数值类型自动转换 // 1.自动提升原则 var c1:Double = 1+2.222 println("c= "+c1) // c= 3.222 // 2.自动类型转换 var d2:Double =2 println("d2= "+d2) // d2= 2.0 // 3.byte short 和 char 不会自动转换 var byte = 64 // byte short 会自动转成 int 进行计算 var bt1:Byte = 60 var sh1:Short = 127 var result:Int= bt1+bt1 println("result ="+result) // result =120 // 强制类型转换 var float1:Float = 2.11111f println("float1 ="+float1) var double1:Double = 2.222 // float1 =2.11111 // 高精度数据转为低精度 需要强制转换 var int2 = double1.toInt println("float2 ="+int2) // float2 =2 // 数值类型和String类型之间转换 // 1.基本类型转 String var a1:Int = 123 var string:String = "hello" var stringResult = a1 + string println("StringResult ="+stringResult) // StringResult =123hello // 2.String 转基本类型 var string2:String = "66" println(string2.toInt) // 66 println(string2.toShort) // 66 println(string2.toDouble) // 66.0 println(string2.toFloat) // 66.0 } }
-
引用类型(AnyRef)
-
8.1.2 运算符
-
算数运算符
-
关系运算符
-
逻辑运算符
-
赋值运算符
-
位运算符
-
Scala运算符的本质
-
实例代码
object TestOperator { def main(args: Array[String]): Unit = { // 1.算数运算 println("加法 =" + (1 + 1)) // 加法 =2 println("减法 =" + (1 - 1)) // 减法 =0 println("乘法 =" + (2 * 2)) // 乘法 =4 println("整数除法=" + (3 / 2)) // 整数除法=1 println("浮点数除法=" + (3 / 2.0)) // 浮点数除法=1.5 println("求余运算= " + (6 % 4)) // 求余运算= 2 // 2.关系运算符 > < >= <== == ! eq println(1 > 2) // false println(1 == 2) // false println(1 >= 2) // false println(1 <= 2) // true println(1 != 2) // true var str1: String = "hello" var str2: String = new String("hello") //Scala完全面向对象,因此都采用方法的方式比较对象,但是scala为了兼容其他语言,仍然保留双等号的功能,但是比较方式发生改变。如果使用双等号进行数据的比较,首先进行非空判断,然后使用equals方法比较。 // 如果需要比较对象的内存地址,使用eq方法 println("字符串比较 =" + (str1 == str2)) // 字符串比较 =true // eq 比较的是引用地址 println("eq 比较 " + (str1.eq(str2))) // eq 比较 false // equals 比较的是字符串的内容是否相等 println("equals 比较" + (str1.equals(str2))) // equals 比较true // 3.逻辑运算符 var boo1 = true var bool2 = false println(boo1 && bool2) // false println(boo1 || bool2) // true println(!(boo1 && bool2)) // true // 4.位运算符 // 5.scala 运算符的本质是函数 // 标准的加法运算 val i: Int = 1.+(1) // (1)当调用对象的方法时,.可以省略 val j: Int = 1 + (1) // (2)如果函数参数只有一个,或者没有参数,()可以省略 val k: Int = 1 + 1 println(1.toString()) // 1 println(1 toString()) // 1 println(1 toString) // 1 } }
Java:==比较两个变量本身的值,即两个对象在内存中的首地址;equals比较字符串中所包含的内容是否相同。
Scala:==更加类似于Java中的equals
Scala中没有++、–操作符,可以通过+=、-=来实现同样的效果
8.1.3 流程控制
-
if else
if else 表达式存在返回值,如果if 和 else的子句类型不一致,Scala会自动进行推断,会取两个类型的公共父类型。
-
switch
Scala不存在Switch,存在模式匹配。
-
for
-
while do while
-
break ,continue
-
多重循环
-
实例代码
import scala.io.StdIn import scala.util.control.Breaks import scala.util.control.Breaks.break object ProcessControl { def main(args: Array[String]): Unit = { // if println("please input a number") var n1 = StdIn.readInt() println("please input your age") var age = StdIn.readInt() // 使用 if 实现 三目运算 val result: Any = if (age < 18) "童年" else "成年" println("result ="+result) var res: Any = null if (n1 < 18) { res = "童年" } else if (n1 > 18 && n1 < 60) { res = "青年" } else { res = 100 } println(res) // for // 1.左闭右b左闭右闭 (To) for (i <- 1 to 10) { println(i) } // 2.左闭右开 范围数据循环(Until) for (i <- 1 until 10) { println(i) } // 3.循环守卫 for (i <- 1 to 10 if i % 2 == 0) { println(i) } // 4. 循环步长 println("循环步长") for (i <- 1 to 10 by 2) { println(i) } // 5.引入变量 for {i <- 1 to 10 j = 10 - i} { println("i=" + i + " j= " + j) } println("多个变量的第二个写法") // 一行有多个语句记得分号隔开,或者使用{ } for (i <- 1 to 10; j = 10 - i) { println("i=" + i + " j= " + j) } // 倒序打印 println("倒序打印第一个方法") for (i <- 10 to 1 by -1) { println("i= " + i) } println("倒序打印第二个方法") for (i <- 1 to 10 reverse) { println("i= " + i) } // 6.循环返回值 yield for 推导式 返回一个集合 var list = for (i <- 1 to 10) yield i println("list =" + list) // scala 中的 break 和 continue // 1.break 使用 println("========break的使用========") Breaks.breakable( for (i <- 1 to 10) { println(i) if (i == 5) { break() } } ) println("=====scala 中continue的使用======") for (i <- 1 to 10) { Breaks.breakable { if (i % 2 == 0) { break() } println(i) } } // do while var sum = 0 var i = 0 do { sum += i i = i + 1 } while (i <= 10) println("sum =" + sum) // while var j = 0 var sum1 = 0 while (j <= 10) { sum1 += j j = j + 1 } println("sum1 =" + sum1) // 多重循环 生成 99 乘法表 for (i <- 1 to 10) { for (j <- 1 to i) { print(j + "*" + i + "=" + (i * j) + "\t") } println() } } }
注意 Scala中break 和 continue 的区别,break使用
()
,continue 使用{}
8.2 Scala函数式编程
8.2.1 函数基础
-
函数基本语法
-
函数和方法的区别
-
为完成某一功能的程序语句的集合,称为函数
-
类中的函数称为方法
-
函数没有重载和重写的概念;方法可以进行重载和重写
-
-
函数定义
-
函数参数
- 默认参数
- 关键字参数(带名参数)
- 可变参数
-
特殊的函数-过程
在Scala中,定义函数时,如果函数体直接在花括号里面而没有使用=连接,则函数的返回值类型就是Unit,这样的函数称之为过程。
// 函数 根据函数至简原则,如果函数体只有一行代码可以省略花括号 def sayHello(name: String) = "Hello, " + name // 函数 def sayHello(name: String): String = "Hello, " + name // 过程 根据函数至简原则,如果函数没有返回值可以省略等号 def sayHello(name: String) { "Hello, " + name } // 过程 过程还有一种写法,就是将函数的返回值类型显式定义为Unit def sayHello(name: String): Unit = "Hello, " + name // 注意以下写法是错误的 = {} 必须保留一个 def sayHello(name: String) "Hello, " + name
过程通常用于不需要返回值的函数。
-
函数至简原则
-
实例代码
object FunctionDemo { def main(args: Array[String]): Unit = { // 1.函数基本语法 def say_hello(args: String): Unit = { println("hello " + args) } say_hello("scala") // 2.函数和方法 def main(): Unit = { // println("hello main") } // 函数不能重写,方法可以重写和重载 // 函数嵌套 def test2(): Unit = { def test1(): Unit = { println("test1方法执行") } } test2() // 3.函数的定义 // 无参,无返回值 def test3_1(): Unit = { println("无参,无返回值") } // 无参,有返回值 def test3_2(): String = { "无参,有返回值" } // 有参,无返回值 def test3_3(age: Int): Unit = { println(s"age is ${age}") } // 有参,有返回值 def test_4(name: String): String = { s"name is ${name}" } println("=======函数定义开始执行========") test3_1() println(test3_2()) test3_3(12) println(test_4("Justin")) // 4.函数的参数 // 可变参数 // 参数默认值 def test4_1(age: Int, s: String*): Unit = { println("age " + age + " s" + s) } println("======可变参数执行=======") test4_1(12, "justin", "lol", "lol66") //age 12 sWrappedArray(justin, lol, lol66) // 利用可变参数 定义求和函数 def add(number: Int*): Int = { var sum = 0; for (num <- number) { sum += num } sum } println(add(1, 2)) println(add(1, 2, 3)) println(add(1, 2, 3, 4, 5)) // 参数默认值 def test4_2(age: Int, name: String = "Justin"): Unit = { println(s"age is ${age},name is ${name}") } test4_2(12, name = "Jack") // 5.函数至简原则 //(1) return可以省略,Scala会使用函数体的最后一行代码作为返回值 def f1( s : String ): String = { s + " jinlian" } println("f1执行"+f1("Hello")) //(2)如果函数体只有一行代码,可以省略花括号 def f2(s:String):String = s + " jinlian" println("f2执行"+f2("Justin")) //(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略) def f3( s : String ) = s + " jinlian" println("f3执行"+f3("Hello3")) //(4)如果有return,则不能省略返回值类型,必须指定。 def f4() :String = { return "ximenqing4" } println("f4执行"+f4()) //(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用 def f5(): Unit = { "dalang5" } println("f5执行 "+f5()) //(6)Scala如果期望是无返回值类型,可以省略等号 // 将无返回值的函数称之为过程 def f6() { "dalang6" } println("f6执行 "+(f6())) //(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加 def f7() = "dalang7" println("f7执行 "+f7()) println("f7执行 "+f7) //(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略 def f8 = "dalang" //println(f8()) println("f8执行 "+f8) //(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略 def f9 = (x:String)=>{println("wusong")} def f10(f:String=>Unit) = { f("") } f10(f9) println(f10((x:String)=>{println("wusong")})) } }
8.2.2 函数高级
-
高阶函数
- 函数作为值传递
- 函数作为参数传递
- 函数作为返回值
-
常见的高阶函数及使用
-
map
对集合传入的每一个元素都进行处理,返回一个
新的集合
var listBuffer_map: ListBuffer[Int] = ListBuffer(1, 2, 3, 4) println(listBuffer_map.map(_ * 2)) // ListBuffer(2, 4, 6, 8) println("listBuffer_map ="+listBuffer_map) // listBuffer_map =ListBuffer(1, 2, 3, 4)
listBuffer_map.map(_ * 2) 等效于 listBuffer_map.map(num=>num*2),_代表每一个元素
-
flatMap
对集合传入的每个元素都进行处理,返回一个或者多个元素
scala> Array("hello you","hello me").flatMap(_.split(" ")) res5: Array[String] = Array(hello, you, hello, me)
-
foreach
对传入的每一个元素都进行处理,没有返回值
scala> Array(1,3,5,7,9).foreach(println) 1 3 5 7 9
一般用于集合的遍历
-
filter
对传入的每个元素都进行条件判断,如果返回true,则保留该元素,否则过滤掉该元素。
scala> Array(1,2,3,4,5,6,7,8,9,10).filter(_ %2 ==0) res7: Array[Int] = Array(2, 4, 6, 8, 10)
Array(1,2,3,4,5,6,7,8,9,10).filter(_ %2 ==0)
等效于Array(1,2,3,4,5,6,7,8,9,10).filter(num=>num%2==0)
-
reduceLeft
从左侧元素开始,进行reduce操作 。
scala> Array(1,2,3,4,5,6,7,8,9,10).reduceLeft(_+_) res10: Int = 55
Array(1,2,3,4,5,6,7,8,9,10).reduceLeft(_+_)
等效于Array(1,2,3,4,5,6,7,8,9,10).reduceLeft((t1,t2)=>t1+t2)
注意:这里的两个_代表是两个元素
-
-
匿名函数
-
函数柯里化和闭包
- 闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包。
- 函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
-
递归
- 一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
-
控制抽象
- 值传递(传递整个函数)
- 名传递(传递函数名)
Java 中只有值传递,Scala中有值传递也名传递
-
惰性函数
Scala提供了lazy特性,如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算。
对于特别耗时的操作,使用lazy非常有用。
-
实例代码
package chapter44 object FunctionDemo1 { def main(args: Array[String]): Unit = { // 高阶函数 // 0.高阶函数 函数作为值传递(有点类似函数表达式) def say_hi(): Unit = { println("hi") } var function1 = say_hi _ function1() // hi // 1.高阶函数 函数作为参数传递 def describle(x: String, y: String): String = { x + "_" + y } // 函数签名,f表示函数名称;(String,String)表示输入两个String参数;String表示函数返回值 def concat(x: String, y: String, f: (String, String) => String): String = { f(x, y) } // 高阶函数调用 println("函数作为参数传递 :" + concat("justin", "LOL", describle)) // 2.高阶函数,函数作为返回值返回 def doSquare(): Int => Int = { (x: Int) => x * x } def add1(): (Int, Int) => Int = { def add2(a: Int, b: Int) = { a + b } // add2 _ 代表整个函数本身,返回一个函数 add2 _ } println(doSquare()(4)) println(add1()(6, 6)) // 匿名函数 var add = (x: Int, y: Int) => x + y println(add(1, 2)) // 函数柯里化 把一个参数列表的多个参数,变成多个参数列表 // 正常写法 def sum_num(x: Int, y: Int): Int = { x + y } // 函数柯里化 的本质 返回的是一个函数 def sum_num2(x: Int): Int => Int = { (y: Int) => x + y } // 函数柯里化写法 def sum_num_1(x: Int)(y: Int): Int = { x + y } println("正常写法调用 " + sum_num(1, 2)) println("函数柯里化调用 " + sum_num_1(1)(2)) // 闭包 println("========闭包的使用======") def f1() = { var a: Int = 10 def f2(b: Int) = { a + b } f2 _ } // 在调用时,f1函数执行完毕后,局部变量a应该随着栈空间释放掉 val f = f1() // 但是在此处,变量a其实并没有在栈空间中释放,而是包含在了f2函数的内部,形成了闭包的效果 println(f(3)) println(f1()(3)) // 递归 def Fibonacci(n: Int): Int = { if (n == 1 || n == 2) 1 else Fibonacci(n - 1) + Fibonacci(n - 2): Int } println("斐波那契数列 " + Fibonacci(5)) // 控制抽象 // 值传递 println("=====值传递=====") def f1_value(): String = { "hello scala" } def f2_value(a: String): Unit = { println(a) } f2_value(f1_value()) // 名传递 println("=====名传递=====") def f1_name(): String = { "hello scala" } // f 为签名函数 def f2_name(f: () => String): Unit = { println(f()) } f2_name(f1_name) // 惰性函数 Scala中使用关键字lazy来定义惰性变量,实现延迟加载(懒加载)。 //惰性变量只能是不可变变量,并且只有在调用惰性变量时,才会去实例化这个变量。 def init(): String = { println("init begin开始了") "hello" } println("=====init begin====") val result1: String = init() lazy val result = init() println("result = " + result) } }
8.3 Scala面向对象
8.3.1 类和对象
-
定义类
-
属性
-
实例代码
import scala.beans.BeanProperty object ObjectDemo { def main(args: Array[String]): Unit = { // 类和对象 class Person { } class Teacher { } class Person2 { var name: String = "bobo" var age: Int = _ // _设置默认值 val 修饰的属性不能赋默认值,必须显示指定 @BeanProperty var sex: String = "男" // @BeanPropetry,可以自动生成规范的setXxx/getXxx方法 } var person2 = new Person2() println(s"name = ${person2.name},age = ${person2.age}") person2.name = "张三" println(s"name = ${person2.name},age = ${person2.age}") person2.setSex("女") println(person2.getSex) } }
8.3.2 封装
-
访问权限
-
方法
-
创建对象
-
构造器
-
主构造器
类似Java的默认构造函数 this(),Scala的主constructor是与类名放在一起的,与Java不同,Java中的构造函数是写在类内部的.
注意:在类中,没有定义在任何方法或者是代码块之中的代码就是主constructor的代码
-
辅助构造器
类似Java的重载构造函数 this(name,age)
-
基本语法
class 类名(**形参列表**) { **//** **主构造器** // 类体 def this(形参列表) { // 辅助构造器 } def this(形参列表) { //辅助构造器可以有多个... } }
-
注意事项
-
辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数及类型来区分。
-
辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。
-
构造器调用其他另外的构造器,要求被调用构造器必须提前声明。
-
-
-
构造器参数
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
-
未用任何修饰符修饰,这个参数就是一个局部变量
-
var修饰参数,作为类的成员属性使用,可以修改
-
val修饰参数,作为类只读属性使用,不能修改
-
-
实例代码
class Person3 { // 封装 ,使用 idea 自动生成 getter setter 方法 //Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。 // 所以Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容, // 也会为Scala的属性设置getXXX和setXXX方法(通过@BeanProperty注解实现)。 private[this] var _name: String = _ def name: String = _name def name_=(value: String): Unit = { _name = value } private[this] var _age: Int = _ def age: Int = _age def age_=(value: Int): Unit = { _age = value } // 重写 toString 方法 override def toString: String = s"name = ${name} age = ${age}" def sum(a: Int, b: Int): Int = { a + b } } // 创建对象 class Person5 { var name: String = "justin" } // 构造器 // 无参构造器可以省略不写 class Person6 { var name: String = _ var age: Int = _ def this(age: Int) { // 辅助构造器 第一行 一定是对先前构造函数的 引用 this() this.age = age println("辅助构造器") } def this(name: String) { this() this.name = name println("辅助构造器") } def this(age: Int, name: String) { this() this.name = name this.age = age } override def toString: String = s"name = ${name} age = ${age} " } // 构造器函数的 参数 /** * * @param name 局部变量 * @param age var 成员属性 可以修改 * @param sex val 类的只读属性 */ class Person7(name:String,var age:Int,val sex:String){ override def toString: String = s" name = ${name} age = ${age} sex = ${sex} " } object ObjectDemo1 { def main(args: Array[String]): Unit = { var person3 = new Person3() // setter方法 person3.name = "张三" person3.age = 12 // person3.name getter 方法 // println(person3.name, person3.age) println("调用toString方法 " + person3) println("person3 sum方法调用 " + person3.sum(1, 2)) val person5 = new Person5() person5.name = "张三" println(person5.name) var person6_1 = new Person6() println("无参构造器 " + person6_1) var person6_2 = new Person6(12) println("age 带参构造器 " + person6_2) var person6_3 = new Person6("Justin") println("name 带参构造器 " + person6_3) var person6_4 = new Person6(12, "Justin") println("age name 带参构造器 " + person6_4) var person7 = new Person7("justin",12,"男") println("person7 "+person7) // name 局部变量 无法访问 // person7.name println(person7.age) // sex Person7类的只读属性 不能修改 // person7.sex = "女" } }
8.3.3 继承
-
实例代码
// 继承 class Human(nameArgs: String) { var name: String = nameArgs var age: Int = _ def this(nameArgs: String, age: Int) { this(nameArgs) this.age = age println("父类的辅助构造器") } println("父类的主构造器") } class Teacher(nameArgs: String, age: Int) extends Human(nameArgs, age) { var sex: String = _ def this(nameArgs:String,age:Int,sex:String){ this(nameArgs,age) this.sex = sex println("子类的辅助构造器") } println("子类的主构造器") } object ObjectDemo2 { def main(args: Array[String]): Unit = { var teacher = new Teacher("张三",12,"男") } }
8.3.4 抽象类
-
抽象属性和抽象方法
//1 定义抽象类: abstract class Person{} //通过abstract关键字标记抽象类 //2 定义抽象属性: val|var name:String //一个属性没有初始化,就是抽象属性 //3 定义抽象方法: def hello():String //只声明而没有实现的方法,就是抽象方法
-
实例代码
// 抽象类 // 抽象方法 // 抽象属性 abstract class Person9{ val name:String // 抽象属性 def hello():Unit // 抽象方法 // 非抽象属性 只能用 val val age:Int = 0 def say_hello:Unit={ println("hello 父类") } } // 抽象类中可以带有参构造函数 这是和特质最重要的区别 abstract class Person99(var name:String){ def say_99():Unit def say_666():Unit={ println("6666"+name) } } class Doctor extends Person9{ // 实现抽象属性 val name:String = "hello" // 实现抽象方法 override 可以省略不写 def hello(): Unit ={ println("hello world") } // 重写父类非抽象方法 override def say_hello: Unit = { println("hello 子类") } // 重写父类的非抽象属性 override val age: Int = 11 } class Doctor99 extends Person99(name = "jjj"){ // 实现抽象方法 override 可以省略不写 def say_99():Unit={ println("hello"+name) } // 重写抽象类中的 非抽象方法 override def say_666(): Unit = println("6666 hello"+name) } object ObjectDemo3 { def main(args: Array[String]): Unit = { var doctor = new Doctor() doctor.say_hello doctor.hello() println("=====抽象类带有参函数======") var doctor99 = new Doctor99() doctor99.say_99() doctor99.say_666() } }
-
匿名子类
// 匿名子类 abstract class Person12 { val name: String def hello(): Unit } class ObjectDemo4 { def main(args: Array[String]): Unit = { var person12 = new Person12 { override val name: String = "Justin" override def hello(): Unit = println(1 + 2) } person12.hello() } }
8.3.5 单例对象(伴生对象)
-
对象-Object
object:相当于class的单个实例,通常在里面放一些静态的field或者method
object不能定义带参数的constructor,只有空参的constructor
第一次调用object的方法时,会执行object的constructor,也就是执行object内部不在任何方法中的代码,因为它只有空参的构造函数
但是注意,object的constructor的代码只会在他第一次被调用时执行一次,以后再次调用就不会再执行了object通常用于作为单例模式的实现,或者放class的一些静态成员,比如工具方法。
可以直接通过Object的名称调用属性或者方法,类似于Java中的静态类。
-
伴生对象概念
Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
-
单例对象基本语法
// 伴生对象 // (1) 伴生对象采用object关键字声明 object ObjectDemo5 { var country: String = "China" } // (2) 伴生对象对应的类称为伴生类,伴生对象的名称应该和伴生类名一致 class ObjectDemo5 { var name: String = "Justin" } object Test_danli { def main(args: Array[String]): Unit = { // (3)伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问(伴生作用) println(ObjectDemo5.country) println(new ObjectDemo5().name) println(1.+(1)) } }
-
apply方法
package chapter55 // 伴生类 //如果想让主构造器变成私有的,可以在()之前加上private class ObjectDemo6 private (cname:String){ var name:String = cname } // 伴生对象 object ObjectDemo6{ def apply():ObjectDemo6={ println("apply空参被调用") new ObjectDemo6("justin") } def apply(name:String):ObjectDemo6={ println("apply有参被调用") new ObjectDemo6(name) } } // 如果通过伴生对象的apply方法创建对象,编译器可以自动识别,此时apply方法名可以省略 object Test_apply{ def main(args: Array[String]): Unit = { // 使用 apply 创建伴生对象 val p1 = ObjectDemo6.apply() var p2 = ObjectDemo6.apply("Justin") var p3 = ObjectDemo6("LOL") // 等效与 ObjectDemo6.apply() apply 可以省略 print(p3) } }
通过伴生对象的apply方法,实现不使用new方法创建对象。
如果想让主构造器变成私有的,可以在()之前加上private。
apply方法可以重载。
Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。
当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。
8.3.6 特质
-
特质概念
Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于Java中的抽象类。Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。
-
基本语法
// 特质 类似接口 trait PersonTrait { var name: String = _ def eat(): Unit = {} var age: Int def say(): Unit={} } trait TraitDemo { var sex: String } //2.一个类可以实现/继承多个特质 //3.所有的Java接口都可以当做Scala特质使用 class Teacher_1 extends PersonTrait with java.io.Serializable{ override def eat(): Unit = println("eat") override def say(): Unit = println("say===") override var age: Int = _ } object TestTrait{ def main(args: Array[String]): Unit = { val teacher = new Teacher_1() teacher.say() teacher.eat // 混入特质 可灵活的扩展类的功能 var teacher_1 = new Teacher_1 with TraitDemo{ override var sex: String = "男" } println(teacher_1.sex) } }
-
特质叠加
// 特质叠加第一种情况 // 一个类(Sub)混入的两个trait(TraitDemo1,TraitDemo1_1)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Teacher_2)中重写冲突方法。 trait TraitDemo1 { def say_hello():Unit={ println("hello") } } trait TraitDemo1_1 { def say_hello():Unit={ println("hello_TraitDemo1_1") } } // 重写冲突方法 class Teacher_2 extends TraitDemo1 with TraitDemo1_1 { override def say_hello(): Unit = println("hello world") } object Test_Trait{ def main(args: Array[String]): Unit = { var teacher_2 = new Teacher_2 teacher_2.say_hello() } }
// 特质叠加第二种情况 // 一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。 trait TraitDemo2 { def describe(): String = "父类特质" } trait TraitDemo2_1 extends TraitDemo2 { var name: String = "TraitDemo2_1" override def describe(): String = name + "_" + super.describe() } trait TraitDemo2_2 extends TraitDemo2 { var sex: String = "TraitDemo2_2" override def describe(): String = sex + "_" + super.describe() } // 继承两种特质 class TraitDemo2_Final extends TraitDemo2_2 with TraitDemo2_1 { override def describe(): String = "TraitDemo2_Final" + "_" + super.describe() } object Test_Trait2 { def main(args: Array[String]): Unit = { var traitDemo2 = new TraitDemo2_Final() println(traitDemo2.describe()) //TraitDemo2_Final_TraitDemo2_1_TraitDemo2_2_父类特质 } }
-
特质叠加执行顺序
如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如super[Category].describe()。
-
特质自身类型
import javafx.collections.ObservableSet //特征自身类型,表示自己的子类型也必须是相应的类型 //产生的效果相当于继承或者混入(mixin) // 特质自身类型,只能混入某个类 // 这里要使用成员属性 var 定义 //定义User类,作为trait的自身类型 class User(var name: String, var age: Int) //定义一个特质,跟数据库打交道,增删改查 trait UserDao { // User 代表 trait的自身类型 this: User => //定义插入数据方法 def insert(): Unit = { println("insert into db :" + this.name) } } class MyUser(name: String, age: Int) extends User(name, age) with UserDao object TraitDemo3 { def main(args: Array[String]): Unit = { val user = new MyUser("f1", 12) user.insert() } }
-
特质和抽象类的区别
优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行(有无参构造)。
8.3.7 扩展
-
类型检查和转换
//类型检查和转换 //(1)obj.isInstanceOf[T]:判断obj是不是T类型。 // //(2)obj.asInstanceOf[T]:将obj强转成T类型。 // //(3)classOf获取对象的类名。 class ObjectDemo7 { } class ObjectDemo77{ } object ObjectDemo7{ def main(args: Array[String]): Unit = { var objectDemo7 = new ObjectDemo7() var objectDemo77 = new ObjectDemo77() // isInstanceOf 判断对象是否为某个类型的实例 var bool:Boolean = objectDemo7.isInstanceOf[ObjectDemo7] if (bool){ // asInstanceOf 将对象转换成某个类型的实例 var p1:ObjectDemo7 = objectDemo7.asInstanceOf[ObjectDemo7] println(p1) // classOf 获取类的信息 var pClass:Class[ObjectDemo7] = classOf[ObjectDemo7] println(pClass) } } }
-
枚举类和应用类
// 枚举类 // 继承类 object ObjectDemo8{ def main(args: Array[String]): Unit = { println(ColorEm.RED) println(ColorEm.values) // type 给类型起别名 type I = Int var age:I = 10 println(s"age = ${age}") } } // 枚举类 object ColorEm extends Enumeration{ val RED = Value(1,"red") val YELLOW = Value(2,"YELLOW") val BLUE = Value(3,"BLUE") } // 应用类 可以直接执行 object他继承了App类,没有写main方法,但是可以生产运行,这是由于App类是Scala自己提供的一个类,它的作用是当object继承它时,不需要写main方法,而是将整个的类看做一个main方法, 可以直接运行 object TestApp extends App { println("xxxx") }
-
Type定义新类型
8.4 Scala异常
-
抛出异常
-
声明异常
-
捕获异常
-
实例代码
object ExceptionDemo { def main(args: Array[String]): Unit = { // 抛出异常 def test(): Nothing = { throw new Exception("erro") } // 声明异常 @throws(classOf[NumberFormatException]) def f11: Unit = { "abc".toInt } // 捕获异常 try { val a = 22 / 0 } catch { case e: ArithmeticException => { println("算术异常") } //算术异常 case e: Exception => { println("其他异常") } } finally { println("运行结束") } } }
8.5 Scala集合
8.5.1 集合简介
-
集合体系
-
集合介绍
1)Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。
2)对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本,分别位于以下两个包
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
3)Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。类似于java中的String对象
4)可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似于java中StringBuilder对象
建议:在操作集合的时候,不可变用符号,可变用方法
-
不可变集合
- 可变集合
-
常见实现类
集合的顶层特质是Iterable,Iterable特质下面还有一些子接口,Set、Seq、Map
set下面有HashSet、LinkedHashSet、SortedSet等等 seq下面有List、Buffer、Range等等 Map下面有HashMap、SortedMap、LinkedHashMap等等 其中Buffer下面还有两个常用的,ArrayBuffer、ListBuffer
8.5.2 数组
-
不可变数组
Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。
-
可变数组
scala 可变集合 集合可以直接对原对象进行修改,而不会返回新的对象。
-
数组转换
toArray:可变数组转不可变数组
toBuffer:不可变数组转可变数组
-
多维数组
-
实例代码
package chapter66 import scala.collection.mutable.ArrayBuffer // 集合 // 不可变 建议使用符号操作 集合 // 可变使用方法 object CollectionDemo { def main(args: Array[String]): Unit = { // 不可变数组 数组长度不变 // 1.定义数组 var array1 = Array(1, 2, 3, 4, 5) // 底层调用 apply var array2 = new Array[Int](5) println(array1.mkString(",")) // 2.数组赋值 array2(0) = 1 array2.update(1, 20) array2.update(2, 30) array2.update(3, 40) array2.update(4, 50) // array2.update(5,6) println("输出数组 =" + array2.mkString(",")) println("数组的长度 = " + array2.length) // 3. 数组遍历 for (i <- array1) { println(i) } array2.foreach((x) => println(x)) // 4.增加元素 println("array1 增加元素前 " + array1.mkString(",")) var ints: Array[Int] = array1 :+ 6 println("增加后的数组 =" + ints.mkString(",")) println("=====不可变数组======") println("array2 增加元素前 " + array2.mkString(",")) var ints2: Array[Int] = 60 +: array2 println("增加后数组 =" + ints2.mkString(",")) // 可变数组 println("=====可变数组=======") // 可变数组定义 var array3 = ArrayBuffer[Any](3, 4, 5) println(array3.mkString(",")) // 可变数组增加元素的方法 .+ append 建议使用 append array3.+=(6) array3.append(7) println(array3.mkString(",")) array3(0)=1 println(array3.mkString(",")) // 可变数组的遍历 array3.foreach((x)=>println(x)) array3.hashCode() println("=====可变数组与不可变数组之间的转换======") // toArray 可变=>不可变 toBuffer 不可变=>可变 var array1_new = array3.toArray var array3_new = array1.toBuffer println(array1_new.mkString(",")) println(array3_new.mkString(",")) // 数组遍历 array3_new.foreach(println) // 多维数据 println("=====多维数组====") // 定义一个数组 类似 ((0,0,0),(0,0,66),(0,0,0),(0,0,0),(0,0,0)) var dim_arr = Array.ofDim[Int](5,3) dim_arr(1)(2) = 66 println("多维数组arr ="+dim_arr.mkString(",")) // [[I@3567135c // 遍历二维数组 for (i <- dim_arr) { // i就是一堆数组 for (j <- i) { println(j + " ") } println() } } }
8.5.3 元组
-
元组概念
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。
注意元组 最大只能有22个元素
-
案例代码
// 元组,可以存放各种相同或不同类型的数据 object CollectionDemo4 { def main(args: Array[String]): Unit = { //声明元组的方式:(元素1,元素2,元素3) val tuple: (Int, String, Boolean) = (20, "bobo", true) //访问元组 //通过元素的顺序进行访问,调用方式:_顺序号 println(tuple._1) println(tuple._2) //通过索引访问数据 println(tuple.productElement(2)) //通过迭代器访问数据 println("=====通过迭代器访问数据=====") for (e <- tuple.productIterator) { println(e) } //Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶.每一对 key value 为元组 val map = Map("a" -> 1, "b" -> 2, "c" -> 3) val map1 = Map(("a" -> 1), ("b" -> 2)) map.foreach(tuple => { println(tuple._1 + "=" + tuple._2) }) map1.foreach(tuple => { println(tuple._1) }) // 遍历元组 map1.foreach(tuple=>println(tuple)) // 元组复制 println(("hello")*2) } }
8.5.4 Set集合
-
概念
Set代表一个没有重复元素的集合。Set集合分为可变和不可变集合。默认情况下使用的是不可变集合。
-
不可变Set
-
可变mutable.Set
-
Set常见子类
HashSet:这个集合的特点是:集合中的元素不重复、无序 LinkedHashSet:这个集合的特点是:集合中的元素不重复、有序,它会用一个链表维护插入顺序,可以保证集合中元素是有序的 SortedSet:这个集合的特点是:集合中的元素不重复、有序,它会自动根据元素来进行排序。
-
实例代码
import scala.collection.mutable // Set 数据不重复,存放相同类型的数据 object CollectionDemo2 { def main(args: Array[String]): Unit = { // 不可变 Set(默认) 无序不重复序列 // 1.创建Set val set: Set[Int] = Set(1, 2, 3) // 2.Set添加元素 var set1: Set[Int] = set + 4 println("set1 =" + set1) // Set(1, 2, 3, 4) var set2: Set[Int] = set + 5 + 6 println("set2 =" + set2) // Set(5, 1, 6, 2, 3) // 3.Set 删除元素 var set3: Set[Int] = set2 - 6 println("set3 =" + set3) // Set(5, 1, 2, 3) // 3.Set合并 var set4: Set[Int] = set1 ++ set2 ++ set3 println("set4 =" + set4) // 4.Set遍历 set4.foreach(println) // 可变 Set // 1.创建可变Set var setBuffer = mutable.Set(1, 2, 3, 4) var setBuffer_1 = mutable.Set(5, 6) // 2.集合添加元素 setBuffer.add(5) setBuffer.add(6) println("setBuffer =" + setBuffer) // 3.集合添加元素 返回一个新的set var setBuffer_2: mutable.Set[Int] = setBuffer_1.+(8) println("setBuffer_2 =" + setBuffer_2) // setBuffer_2 =Set(5, 6, 8) // 4.删除数据 setBuffer_2 -= 6 println("setBuffer_2 =" + setBuffer_2) // setBuffer_2 =Set(5, 8) println("删除前 setBuffer_1 =" + setBuffer_1) // 删除前 setBuffer_1 =Set(5, 6) setBuffer_1.remove(5) println("删除后 setBuffer_1 =" + setBuffer_1) // 删除后 setBuffer_1 =Set(6) // 5.合并两个set var set_add: mutable.Set[Int] = setBuffer ++ setBuffer_1 println("set_add =" + set_add) // 6.set 取并集 var set11: mutable.Set[Int] = mutable.Set(1, 2, 3, 4) var set22: mutable.Set[Int] = mutable.Set(3, 4, 5, 6) println(set11.union(set22)) } }
8.5.5 Seq集合
-
不可变List
-
可变ListBuffer
-
实例代码
import scala.collection.mutable.ListBuffer // Seq 集合(List) object CollectionDemo1 { def main(args: Array[String]): Unit = { // 不可变List // 创建list var list: List[Int] = List(1, 2, 3, 4, 5) // 底层伴生对象 实现了 apply 方法 所以可以直接这样定义 等效于 List.apply() // println("list ="+list) // 遍历 List list.foreach(println) // List 末尾增加数据 首加7 末尾加6 var list1: List[Int] = 7+:(list :+ (6)) var list2:List[Int] = 7::6::list println("list1 "+list1.mkString(",")) println("list2 "+list2.mkString(",")) // 增加到第一个元素位置 // List合并 ::: var list3:List[Int] = list1 ::: list2 println("list3 "+list3) // 取出指定的数据 println(list3(0)) // 可变 ListBuffer 可变建议使用方法 println("=====可变ListBuffer=====") var listBuffer:ListBuffer[Any] = ListBuffer(1,2,3,4) // 增加数据 listBuffer.append(5) println("listBuffer ="+listBuffer) // listBuffer =ListBuffer(1, 2, 3, 4, 5) listBuffer.append(6,7,8,9,10) println("listBuffer ="+listBuffer) // listBuffer =ListBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // 插入数据 listBuffer.insert(0,0) println("listBuffer ="+listBuffer) // listBuffer =ListBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // ListBuffer 更新数据 listBuffer.update(0,100) println("listBuffer ="+listBuffer) // listBuffer =ListBuffer(100, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // ListBuffer 移除指定索引的数据 listBuffer.remove(0) // ListBuffer 遍历数据 listBuffer.foreach(println) } }
8.5.6 Map集合
-
概念
Map是一种可迭代的键值对(key、value)结构,Map分为可变和不可变,默认情况下使用的是不可变Map。
-
不可变Map
-
可变Map
-
Map常见子类
HashMap:是一个按照key的hash值进行排列存储的map SortedMap:可以自动对Map中的key进行排序【有序的map】 LinkedHashMap:可以记住插入的key-value的顺序
-
实例代码
import scala.collection.mutable // map 无序散列表 key 不能重复 object CollectionDemo3 { def main(args: Array[String]): Unit = { // 创建不可变 map val map = Map("a" -> 1, "b" -> 2, "c" -> 3) println("map =" + map) // 遍历map map.foreach(println) map.foreach((kv: (String, Int)) => println(kv)) for (elem <- map.keys) { println("value " + map.get(elem).get) } // 使用模式匹配进行 map 遍历 println("=====使用模式匹配进行 map 遍历========") for((k,v) <- map){ println(k +":"+ v) } //访问具体的数据 使用get访问map集合的数据,会返回特殊类型Option(选项):有值(Some),无值(None) println("key a =" + map.get("a").get) // getOrElse 如果值不存在 返回 0 println("key d =" + map.getOrElse("d", 0)) // key d =None // 创建可变map // val map_Buffer = mutable.Map("a"->1,"b"->2) // 创建一个空的 map val map_Buffer: mutable.Map[String, Int] = mutable.Map() // 增加数据 4种方式 推荐使用 put map_Buffer.+=("a" -> 1) map_Buffer.+=("b" -> 2) map_Buffer.+=("c" -> 3) map_Buffer.+=(("c", 4)) map_Buffer.put("d", 5) map_Buffer("z") = map_Buffer.getOrElse("z", 0) println("map_Buffer =" + map_Buffer) map_Buffer.put("e", 6) println("map_Buffer =" + map_Buffer) // 获取数据 建议使用 getOrElse() 获取数据 println("map_Buffer e =" + map_Buffer.get("e").get) println("map_Buffer e =" + map_Buffer.getOrElse("e", 0)) // 删除数据 两种 方法 推荐使用 remove map_Buffer.-=("e") println(map_Buffer) map_Buffer.remove("d") println(map_Buffer) // 修改数据 println("======修改数据=====") map_Buffer.update("c", 44) println(map_Buffer) // map 转 List println(map_Buffer.toList) } }
8.5.7 集合常用函数
-
基本属性和常用操作
-
衍生集合
-
集合计算初级函数
-
集合计算高级函数
-
实例代码
import scala.collection.mutable // 集合的基本操作 object CollectionDemo5 { def main(args: Array[String]): Unit = { var list1 = List(1, 2, 3, 4, 5) var list2 = List(3, 4, 5, 6, 7) // 获取集合的长度 list.length println("集合list1的长度为 " + list1.length) // 获取集合的大小 list.size println("集合list1的大小为 " + list1.size) // 循环遍历 foreach list1.foreach(println) // 迭代器 list.iterator for (i <- list1) { println(i) } for (ele <- list1.iterator) { println("ele =" + ele) } // 生成字符串 mkString println("生成字符串 mkString=" + list1.mkString(",")) // 是否包含 contains println("是否包含 contains =" + list1.contains(1)) // 获取集合的头 list.head println("获取集合的头 list.head " + list1.head) // 获取集合的尾 last println("获取集合的头 list.last " + list1.last) // 集合的最后一个元素 tail(除集合第一个元素外的以外元素) println("除集合第一个元素外的以外元素 " + list1.tail) // 集合数据(不包含最后一个) list.innit println("不包含最后一个集合数据 " + list1.init) // 反转 list.reverse 返回一个新的集合对象 println("反转 list.reverse 返回一个新的集合对象 " + list1.reverse) // 取前(后)n个元素 list.take(n) println("取前3个元素 = " + list1.take(3)) println("取后三个元素 =" + list1.takeRight(3)) // 去掉前(后)n个元素 list.drop println("去除前3个元素 =" + list1.drop(3)) println("去除后三个元素" + list1.dropRight(3)) // 并集 list.union(list1) println("集合的并集" + list1.union(list2)) // 交集 list.intersect(list1) println("集合的交集 =" + list1.intersect(list2)) // 差集 list.diff(list1) println("集合的差集 =" + list2.diff(list1)) // 拉链 list.zip(list2) println("集合的拉链 = " + list1.zip(list2)) // 滑窗 list1.sliding(2, 5).foreach(println) list1.sliding(n,m), n 为每个集合的元素个数 m 为 相较上一个集合第一个元素位移的大小 list1.sliding(2, 1).foreach(println) // 求和 sum println("求和 =" + list1.sum) // 求乘积 product println("求乘积 =" + list1.product) // 最大值 max println("最大值 =" + list1.max) // 最小值 min println("最小值 =" + list1.min) // 排序 sortBy 对一个属性或者多个属性进行排序 println("降序排序 ="+list1.sortBy(x=>x).reverse) println("升序排序 ="+list1.sortBy(x=>x)) var list3 = List(1,-5,2,3,-3,-1) println("绝对值大小排序 "+list3.sortBy(x=>x.abs)) // sortWith 基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑。 println("升序排序 "+list3.sortWith((x,y)=>x<y)) println("降序排序 "+list3.sortWith((x,y)=>x>y)) // 过滤 filter var list4 = List(1,2,3,4,5,6,7,8,9,10) println("过滤 filter = "+ list4.filter(x=>x%2==0)) // 转化 map println("转换 amp = "+ list4.map(x=>x*10)) // 扁平化 flatten var nestList = List(List(1,2,3),List(4,5,6),List(7,8,9)) println("扁平化 "+nestList.flatten) // 扁平化加映射 var wordList = List("hello world","hello scala","hello Java") println(wordList.flatMap(x=>x.split(" "))) // 分组 groupBy println(list4.groupBy(x=>x%2)) // 简化 reduce 底层调用 reduceLeft() 方法 var list5 = List(1,2,3,4,5) println(list5.reduce((x,y)=>x+y)) // 折叠 fold var list6 = List(1,2,3,4,5,6) println(list6.fold(1){(x,y)=>x+y}) // 两个Map的数据合并 val map1 = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3) val map2 = mutable.Map("a" -> 4, "b" -> 5, "d" -> 6) val map3: mutable.Map[String, Int] = map2.foldLeft(map1) { // 这里的形参 map 指的是 map1 (map, kv) => { val k = kv._1 // println("map ="+map) // print("k ="+k) val v = kv._2 // println("v = "+v) map(k) = map.getOrElse(k, 0) + v map } } println("map3 ="+map3) } }
8.5.8 WordCount
-
案例一
// wordCount 例子 object CollectionDemo6 { def main(args: Array[String]): Unit = { // 单词计数:将集合中出现的相同的单词,进行 计数,取计数排名前三的结果 val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello") // 1.将每一个字符串转换成一个一个单词 val wordList: List[String] = stringList.flatMap(str => str.split(" ")) println("wordList = "+wordList) // 2.将相同的单词放置在一起 val wordToWordsMap: Map[String, List[String]] = wordList.groupBy(word => word) println("wordToWordsMap ="+wordToWordsMap) // 3.对相同的单词进行计数 val wordToCountMap: Map[String, Int] = wordToWordsMap.map(tuple => (tuple._1, tuple._2.size)) println("wordToCountMap ="+wordToCountMap) // 4.对计数完成的结果进行排序(降序) val sortList: List[(String, Int)] = wordToCountMap.toList.sortWith { (left, right) => { left._2 > right._2 } } println("sortList ="+ sortList) // 5.对排序后的结果取前3名 val resultList: List[(String, Int)] = sortList.take(3) println("resultList ="+resultList) } }
-
案例二
// worldCount 2 object CollectionDemo7 { def main(args: Array[String]): Unit = { // 第一种方式(不通用) val tupleList = List(("Hello Scala Spark Word", 4), ("Hello Scala Spark", 3), ("Hello Scala", 2), ("Hello", 1)) val stringList: List[String] = tupleList.map(t => (t._1 + " ") * t._2) println("tupleList ="+stringList) val words: List[String] = stringList.flatMap(_.split(" ")) println("words = "+words) // 在map中,如果传进来什么就返回什么,不要用 _ 省略 val groupMap: Map[String, List[String]] = words.groupBy(word => word) println("groupMap ="+groupMap) // val groupMap1: Map[String, List[String]] = words.groupBy(_) // (word, list) => (word, count) val wordToCount: Map[String, Int] = groupMap.map(t => (t._1, t._2.size)) println(wordToCount) val wordCountList: List[(String, Int)] = wordToCount.toList.sortWith { (Left, Right) => { Left._2 < Right._2 } }.take(3) // tupleList.map(t => (t._1 + " ") * t._2).flatMap(_.split(" ")).groupBy(word => word).map(t => (t._1, t._2.size)) println("wordCountList="+wordCountList) } }
-
案例三
// worldCount3 object CollectionDemo8 { def main(args: Array[String]): Unit = { val tuples = List(("Hello Scala Spark World", 4), ("Hello Scala Spark", 3), ("Hello Scala", 2), ("Hello", 1)) // (Hello,4),(Scala,4),(Spark,4),(World,4) // (Hello,3),(Scala,3),(Spark,3) // (Hello,2),(Scala,2) // (Hello,1) val wordToCountList: List[(String, Int)] = tuples.flatMap { t => { val strings: Array[String] = t._1.split(" ") strings.map(word => (word, t._2)) } } println("wordToCountList "+wordToCountList) // Hello, List((Hello,4), (Hello,3), (Hello,2), (Hello,1)) // Scala, List((Scala,4), (Scala,3), (Scala,2) // Spark, List((Spark,4), (Spark,3) // Word, List((Word,4)) val wordToTupleMap: Map[String, List[(String, Int)]] = wordToCountList.groupBy(t => t._1) println("wordToTupleMap ="+wordToTupleMap) val stringToInts: Map[String, List[Int]] = wordToTupleMap.mapValues { datas => datas.map(t => t._2) } println("stringToInts ="+stringToInts) val wordToCount: Map[String, Int] = stringToInts.map(t => (t._1, (t._2).sum)) println("wordToCount "+wordToCount.toList.sortWith( (Left, Right) => { Left._2 > Right._2 }).take(3)) /* val wordToCountmap: Map[String, List[Int]] = wordToTupleMap.map { t => { (t._1, t._2.map(t1 => t1._2)) } } val wordToTotalCountMap: Map[String, Int] = wordToCountmap.map(t => (t._1, t._2.sum)) println(wordToTotalCountMap)*/ } }
8.5.9 队列
-
实例代码
import scala.collection.mutable // 队列 先进先出 object CollectionDemo9 { def main(args: Array[String]): Unit = { var que = new mutable.Queue[String]() // 进队列 que.enqueue("a", "b", "c") // 出队列 println(que.dequeue()) println(que.dequeue()) println(que.dequeue()) } }
8.5.10 并行集合
-
实例代码
object CollectionDemo10 { def main(args: Array[String]): Unit = { val result1 = (0 to 100).map { case _ => Thread.currentThread.getName } val result2 = (0 to 100).par.map { case _ => Thread.currentThread.getName } println(result1) //只有一个线程 串行 println("--------------------") println(result2) //并行 } }
8.5.11 集合总结
-
可变集合
LinkedHashSet
,ListBuffer
,ArrayBuffer
,LinkedHashMap
-
不可变集合
List
,SortedMap
-
可变+不可变集合
Set
,HashSet
,SortedSet
,Map
,HashMap
-
编外数据结构
-
Array(数组)
长度不可变,里面的元素可变
-
Tuple(元组)
长度不可变,里面的元素也不可变
-
8.6 Scala高级特性
8.6.1 模式匹配
-
概念
模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的switch case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。不过Scala没有Java中的switch case语法,但是,Scala提供了更加强大的match case语法,就是这个模式匹配。
-
基本语法
注意:match case中,只要一个case分支满足并处理了,就不会继续判断下一个case分支了,这一点与Java不同,java的switch case需要用break停止向下执行。
-
常见的模式匹配类型
-
对变量的值进行模式匹配
def demo1(day: Int) { day match { case 1 => println("Monday") case 2 => println("Tuesday") case 3 => println("Wednesday") // 如果值为下划线,则代表了不满足以上所有情况下的默认处理 case _ => println("none") } }
-
变量类型的模式匹配
import java.io._ def processException(e: Exception) { e match { case e1: IllegalArgumentException => println("IllegalArgumentException " + e1) case e2: FileNotFoundException => println("FileNotFoundException " + e2) case e3: IOException => println("IOException " + e3) case _: Exception => println("Exception " ) } } // 模式匹配 在 try catch 中应用 try { val lines02 = scala.io.Source.fromFile("D://test02.txt").mkString } catch { case ex: FileNotFoundException => println("no file") case ex: IOException => println("io exception") case ex: Exception => println("exception") }
模式匹配 在异常处理中的一个典型应用
-
case class 与模式匹配
// 使用 样例类 匹配对象 case class User02(name: String, age: Int) object MathCaseDemo3 { def main(args: Array[String]): Unit = { val user = User02("张三", 12) val result = user match { case User02("张三", 12) => "yes" case _ => "no" } println(result) } }
-
Option与模式匹配
Scala有一种特殊的数据类型,叫做Option。Option有两种值,一种是Some,表示有值,一种是None,表示没有值 Option通常会用于模式匹配中,用于判断某个变量是有值还是没有值,这比null来的更加简洁明了。
val ages = Map("jack" -> 18, "tom" -> 30, "jessic" -> 27) def getAge(name: String) { val age = ages.get(name) age match { case Some(age) => println("your age is " + age) case None => println("none") } }
-
-
模式守卫
package chapter77 // 模式匹配 math case object MatchCaseDemo { def main(args: Array[String]): Unit = { var a: Int = 10 var b: Int = 20 var operator: Char = 'd' var result = operator match { case '+' => a + b case '-' => a - b case '*' => a * b case '/' => a / b // default case _ => "illegal" } println("result ="+result) // 模式守卫 def abs(x: Int) = x match { case x: Int if x >= 0 => x case x: Int if x < 0 => -x case _ => "type illegal" } println(abs(-5)) } }
-
模式匹配类型
package chapter77 object MatchCaseDemo1 { def main(args: Array[String]): Unit = { // 匹配常量 def describe(x: Any) = x match { case 5 => "Int five" case "hello" => "String hello" case true => "Boolean true" case '+' => "Char +" } println(describe(5)) // 匹配类型 def describe_Type(x: Any): String = x match { case x: Int => "Int" case x: Double => "double" case x: String => "string" case x: Array[Int] => "Array[Int]" // _ 可以匹配 所有类型 case x: List[_] => "List" case something => "something else" + something } //泛型擦除 List 泛型擦除 println(describe_Type(List(1, 2, 3, 4))) // 数组例外,可保留泛型 println(describe_Type(Array(1, 2, 3, 4, 5, 6))) println(describe_Type(Array("abc"))) // 匹配数组 for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { //对一个数据集合进行遍历 val result = arr match { case Array(0) => "0" // 匹配Array(0) 这个数组 case Array(x, y) => x + "," + y // 配置有两个元素的数组,然后将元素赋值给对应的x,y case Array(0, _*) => "以0开头的数组" // 匹配以0开头的数组 case _ => "something else" } println("result=" + result) } // 匹配列表 // list是一个存放List集合的数组 for (list <- Array(List(0), List(0, 0, 0), List(1, 0, 0), List(88))) { val result = list match { case List(0) => "0" // 匹配List(0) case List(x, y) => x + "," + y // 匹配有两个元素的List case List(0, _*) => "0 ..." case List(x) if x == 88 => x // 匹配 88 并返回 case _ => "something else" } println("匹配列表 " + result) } var list1: List[Int] = List(1, 2, 3, 4, 5, 6, 7) list1 match { case first :: second :: rest => println(first + "_" + second + "_" + rest) case _ => println("something else") } // 匹配元组 for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) { val result = tuple match { case (0, _) => "0 ..." // 是第一个元素是0的元组 case (y, 0) => "" + y + " 0" // 匹配后一个元素是0的对偶元组 case (a, b) => "" + a + "" + b case _ => "something else" //默认 } println(result) } println("=====元组的特殊匹配=====") // 特殊的模式匹配1,打印元组第一个元素 for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) { println("特殊的模式匹配1,打印元组第一个元素 " + elem._1) } for ((word, count) <- Array(("a", 1), ("b", 2), ("c", 3))) { println("word couunt " + word) } for ((word, _) <- Array(("a", 1), ("b", 2), ("c", 3))) { println("word " + word) } for (("a", count) <- Array(("a", 1), ("b", 2), ("c", 3))) { println(count) } println("-------------") // 特殊的模式匹配2,给元组元素命名 var (id, name, age): (Int, String, Int) = (100, "zs", 20) println((id, name, age)) println("--------------") // 特殊的模式匹配3,遍历集合中的元组,给count * 2 var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3)) println(list.map { case (word, count) => (word, count * 2) }) val list11 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3))) println(list11.map { case (groupkey, (word, count)) => (word, count * 2) }) var list22 = List(("hello", 4), ("scala", 5)) println(list22.map { case (groupKey,count)=>((groupKey+" ")*count) }) // 匹配对象和样例类 } }
// 模式匹配 匹配对象 class User(val name:String,val age:Int) // 伴生对象 使用 apply 和 unapply 方法 object User{ def apply(name:String,age:Int):User = new User(name, age) // 对象提取器 def unapply(user:User):Option[(String,Int)]={ if (user == null) None else Some(user.name,user.age) } } object MatchCaseDemo2 { def main(args: Array[String]): Unit = { val user:User = User("Justin",18) // 实际调用的是 伴生对象的apply 方法 val result = user match { case User("Justin", 18) => "yes" case _ => "no" } println(result) } }
-
变量声明中的模式匹配
-
for 表达式中的模式匹配
// 变量声明中的模式匹配 case class Person(name: String, age: Int) object MatchCaseDemo04 { def main(args: Array[String]): Unit = { val (x, y) = (1, 2) println(s"x=${x},y=${y}") val Array(first, second, _*) = Array(1, 2, 3, 4, 5, 6) println(s"first = ${first} second = ${second}") val Person(name, age) = Person("zhangsan", 16) println(s"name = ${name} age = ${age}") // for 表达式中的模式匹配 val map = Map("A" -> 1, "B" -> 0, "C" -> 3) for ((k, v) <- map) { // 直接将map中的k-v遍历出来 println(k + "->" + v) } println("---------") // 遍历value = 0 的k-v,如果v不是0,过滤 for ((k, 0) <- map) { println(k + "-->" + 0) } println("----------") for ((k, v) <- map if v >= 1) { println(k + "-->" + v) } // map 和模式匹配结合 // 将该List(1,2,3,4,5,6,“test”)中的Int类型的元素加一,并去掉字符串。 val list = List(1, 2, 3, 4, 5, 6, "test") val list1: List[Any] = list.map { case int: Int => int + 1 case string: String => string + 1 } println(list1.filter(a => a.isInstanceOf[Int])) //方法一 List(1, 2, 3, 4, 5, 6, "test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(print) println() //方法二 List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(print) // 偏函数 ??? } }
8.6.2 样例类
-
概念
1.样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如apply、unapply、toString、equals、hashCode和copy。
2.样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法。
3.构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做) -
实例代码
// 使用 样例类 匹配对象 case class User02(name: String, age: Int) object MathCaseDemo3 { def main(args: Array[String]): Unit = { val user = User02("张三", 12) val result = user match { case User02("张三", 12) => "yes" case _ => "no" } println(result) } }
8.6.3 偏函数
- pass
8.6.4 隐式转换
-
概念
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译
类型转换,调用可以编译通过的方法
-
隐式函数
// 隐式类 // 通过隐式转化 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 TransformDemo { // 使用implicit关键字声明的函数称之为隐式函数 implicit def convert(arg: Int): MyRichInt = { new MyRichInt(arg) } def main(args: Array[String]): Unit = { println(2.myMax(6)) // 6 } }
-
隐式参数
// 隐式参数 // 1.同一个作用域中,相同类型的隐式值只能有一个 // // 2.编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。 // // 3.隐式参数优先于默认参数 object TransformDemo01 { // 隐式变量 str implicit val str:String = "hello world" def hello(implicit arg:String="good by world"):Unit={ println(arg) } def main(args: Array[String]): Unit = { // 隐式值也叫隐式变量,将某个形参表示为implicit,编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺省参数 hello // 使用小括号会导致隐式值无法专递,实际上就是调用了传参 hello() } }
-
隐式类
// 隐式类 object TransformDemo02 { implicit class MyRichInt(arg: Int) { def myMax(i: Int): Int = { if (arg < i) i else arg } def myMin(i: Int): Int = { if (arg < i) arg else i } } def main(args: Array[String]): Unit = { println(1.myMax(3)) } }
-
隐式解析机制
package chapter99 // 隐式解析机制 // 1.首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。(一般是这种情况) // 2.如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象。 import chapter99.TransformDemo03.Teacher object TransformDemo03 extends PersonTrait { def main(args: Array[String]): Unit = { // 1.首先会在当前代码作用域下查找隐式实体 val teacher = new Teacher() teacher.eat() // 调用Person5的方法 teacher.say() } class Teacher { def eat() = { println("eat...") } } } trait PersonTrait { } object PersonTrait { //隐式类:类型1=>类型2 implicit class Person5(user: Teacher) { def say(): Unit = { println("say...") } } }
8.6.5 泛型
- 协变和逆变
- 泛型上下限
- 上下文限定