Scala:基础知识03
Posted Xiao Miao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala:基础知识03相关的知识,希望对你有一定的参考价值。
文章目录
Scala基础知识
一、模式匹配
模式匹配类似于java中switch case 根据的内容进行匹配 匹配到执行结果。
- java switch case
import java.util.Scanner;
public class Six {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("你的成绩是: \\t");
char grade = sc.next().charAt(0);
switch (grade) {
case 'A':
System.out.println("优秀");
break;
case 'B':
case 'C':
System.out.println("良好");
break;
case 'D':
System.out.println("及格");
break;
case 'F':
System.out.println("你需要再努力努力");
break;
default:
System.out.println("输入错误,请重新输入");
}
}
}
1.匹配内容
import scala.io.StdIn
object MatchCase01 {
def main(args: Array[String]): Unit = {
print("请输入内容:\\t")
//从控制台 stdin(标准输入)接收用户输入的内容 根据内容进行模式匹配
val result: String = StdIn.readLine() // 这是个阻塞的方法
result match {
case "hadoop" => println("001")
case "spark" => println("002")
case "flink" => println("003")
case _ => println("输入错误") //相当于default
}
}
}
2.匹配数据类型
import scala.util.Random
object MatchCase02 {
def main(args: Array[String]): Unit = {
//从集合中进行随机取值 根据取值的结果类型进行匹配
val a = Array(11, 22, "hello", "bye", true, false, 3.14, 6.33)
// Random.nextInt() //从0 到(n-1)随机产生一个整数
val result: Any = a(Random.nextInt(a.length))
result match {
case x: Int if x > 11 => println(x) //todo 可以在匹配的时候加上守卫条件 进行过滤
case y: String => println(y) //todo 不满足守卫条件怎么办? 自动落入default中
case z: Double => println(z)
case w: Boolean => println(w)
case _ => println("匹配异常")
}
}
}
3.匹配数组、集合
object MatchCase03 {
def main(args: Array[String]): Unit = {
val a1 = Array(1, 2, 3)
//如果多个条件都满足 从上往下进行匹配 匹配成功 就执行后面的逻辑 不再继续匹配
a1 match {
case Array(2, x, y) => println(x + " " + y)
case Array(1, 2, 3) => println("精准匹配")
case Array(1, _*) => println("1 ...")
case _ => println("something else")
}
val l1 = List(0, 1, 2)
l1 match {
case 0 :: Nil => println("只有0的列表") //List(0)
case 0 :: tail => println("0开头的列表") //List(0,_*)
case x :: y :: Nil => println(s"只有另两个元素${x}, ${y}的列表") //List(x,y)
case _ => println("未匹配")
}
}
}
4.变量声明时进行模式匹配
object MatchCase04 {
def main(args: Array[String]): Unit = {
//todo result是程序处理返回的结果 需要提取当中第一个、第二个元素作为参数 进行后续的计算
val result: Array[Int] = (1 to 5).toArray
//val x = result(0)
//val y = result(1)
val Array(x,y,_*) = result
println(x+y)
}
}
二、apply和unapply方法
1.apply方法
- 背景:为什么在创建集合的时候 有初始值不需要new
- 功能:某种程度上也是一种构造方法,当创建对象的时候 如果不加new底层就会尝试寻找apply方法,如果有,就调用apply方法实现对象的创建
object ApplyCase01 {
def main(args: Array[String]): Unit = {
val a1 = new Array[String](4) //没有初始值 需要new
/**
* def apply(x: Int, xs: Int*): Array[Int] = {
* val array = new Array[Int](xs.length + 1)
* array(0) = x
* var i = 1
* for (x <- xs.iterator) { array(i) = x; i += 1 }
* array
* }
*/
//todo 底层调用apply方法帮助我们去new
val a2: Array[Int] = Array(11, 22, 33) //有初始值 不需要new
}
}
object ApplyCase02 {
def main(args: Array[String]): Unit = {
val miao= new Person("miao") //因为Person是普通class 创建对象实例的时候必须new
//todo 如何实现不new 也能创建呢? 需要自己实现apply方法
val liu: Person =Person("liu") //底层调用apply方法完成了new
val s = Student //为什么这里不需要new 因为它是object 是全局唯一实例 单列
}
}
//todo apply方法必须定义在类的伴生对象中
object Person{
//apply方法
def apply(name: String): Person = new Person(name)
}
class Person(var name:String)
object Student
2.unapply方法
- 功能:从对象中提取属性 相当于提取器 和apply方法功能正好相反
import scala.util.Random
class Student(var name:String)
case class Teacher(var name:String)
case object Maomao
object Student {
//apply方法 实现了 属性--->对象实例
def apply(name: String): Student = new Student(name)
//unapply方法 实现了 对象实例----->属性
def unapply(s: Student): Option[String] = {
Some(s.name)
}
}
object Case02 {
def main(args: Array[String]): Unit = {
// 使用样例类、参与模式匹配
val a:Array[Object]=Array(Student("张三"),Teacher("miao"),Teacher("liu"),Maomao)
val result:Object=a(Random.nextInt(a.length))
result match{
case Teacher(name)=>println(name)
case Student(name)=>println(name)
case Maomao=>println("女神")
case _=>println("error")
}
}
}
三、样例类
样例类是scala中一种特殊的类 使用关键字case修饰 用于封装数据参与模式匹配 传递数据
-
具体来说 样例类分为两种
- case class 多例的 需要 构造器参数列表
- case object 单列的 不需要构造器
-
探究:普通类和样例类之间有什么区别,加上case关键字之后,底层发生了什么?
- 样例类自动实现了apply方法不需要new
- 样例类自动实现了toString方法 之间打印输出属性 而不是地址
- 样例类自动实现了hashcode和equals方法 对象实例可以直接比较 而不是比较地址
样例类自动实现了copy方法
class Cat(var name:String){
override def toString = s"Cat($name)"
def canEqual(other: Any): Boolean = other.isInstanceOf[Cat]
override def equals(other: Any): Boolean = other match {
case that: Cat =>
(that canEqual this) &&
name == that.name
case _ => false
}
override def hashCode(): Int = {
val state = Seq(name)
state.map(_.hashCode()).foldLeft(0)((a, b) => 31 * a + b)
}
}
//样例类
case class Dog(var name:String){
}
object Case1 {
def main(args: Array[String]): Unit = {
val jf1 = new Cat("jiafei") //todo 1、普通的类需要new 才能创建实例
println(jf1) //todo 2、普通的类直接输出打印 针对是地址 如果需要输出属性 重写对象的toString方法
val jf2 = new Cat("jiafei")
println(jf1 == jf2 ) //todo 3、普通对象直接比较 比较的是地址 如果需要指定比较规则 需要重写对象的hashcode和equals方法
/**
* todo 为什么需要重写两个方法进行比较?
* 首先比较hashcode值 如果不一样 肯定不一样 如果一样 有可能不一样 再比较equals方法
*/
val eh1 = Dog("erha") //todo A、样例类底层自动实现apply方法 在创建对象实例的时候可以不加new
println(eh1) //todo B、样例类底层自动实现了toString方法
val eh2 = Dog("erha")
println(eh1 == eh2) //todo C、样例类底层自动实现了hashcode 和equals方法
}
}
样例类核心功能:封装数据
class Student(var name:String)
case class Teacher(var name:String)
case object MaoMao
//定义Student的伴生对象 里面定义apply方法和unapply方法
object Student{
//apply方法 实现了 属性--->对象实例
def apply(name: String): Student = new Student(name)
//unapply方法 实现了 对象实例----->属性
def unapply(s: Student): Option[String] = {
Some(s.name)
}
}
object Case2 {
def main(args: Array[String]): Unit = {
//使用样例类 参与模式匹配
val a: Array[Object] = Array(Student("张三"), Teacher("miao"), Teacher("liu"), Maomao)
val result: Object = a(Random.nextInt(a.length))
result match {
case Teacher(name) => println(name) //可以直接提取对象里面的属性 todo 底层样例类还实现了unapply方法 提取器
case Student(name) => println(name)
// case s:Student => println(s.name) //不行 先匹配对象 然后调用对象.属性
case Maomao=>println("女神")
case _ => println("error")
}
}
}
option类型
- "量子叠加类型"表示可有可无两种状态
- 分为两个子类
- 有 some
- 无 none
object OptionsCase01 {
def main(args: Array[String]): Unit = {
val m = Map("name" ->"liu","age" ->"18")
//根据key去map中取值: 有值 none
val maybeString: Option[String] = m.get("name")
println(maybeString)
}
}
object OptionsCase02 {
def main(args: Array[String]): Unit = {
def m1(x:Int,y:Int) =x / y
// println(m1(4,0))
//如何使用option类型封装程序 让程序更加健壮 鲁棒性
def m2(x:Int,y:Int): Option[Int] ={
if( y != 0) Some(x/y)
else None
}
val maybeInt: Option[Int] = m2(4, 0)
maybeInt match {
case Some(s) =>println(s)
case None => println("除数不能为0")
}
}
}
偏函数
partila Funcation 部分的,不完全的
-
偏函数不是函数,是一种特殊的数学符号现象,可以优化程序代码
-
偏函数从现象上看:没有match却有case的一组语句
-
偏函数是PartialFunction[A, B]的一个实例
-
A代表输入参数类型
-
B代表返回结果类型
object PF01 {
def main(args: Array[String]): Unit = {
val a1: Array[Int] = Array(11,22,33)
println(a1.map(_*10).toBuffer) //在IDEA中 Array对象无法直接打印内容 可以转换成为ArrayBuffer查看
val a2: Array[Any] = Array(11,22,"USA",33)
// println(a2.map(_*10).toBuffer)
println(a2.filter(x => x.isInstanceOf[Int]).map(x => x.asInstanceOf[Int]).map(_ * 10).toBuffer)
//todo 使用偏函数实现上述逻辑 所谓的偏指的是对整体中部分应用
val pf1:PartialFunction[Any,Int] ={
case i:Int => i * 10
}
println(a2.collect(pf1).toBuffer) //针对偏函数的调用 使用collect方法 相当于map方法功能 只不过只接收偏函数作为参数
}
}
object PF02 {
def main(args: Array[String]): Unit = {
//用户输入1,2,3 返回一 二 三
//todo 实现1:使用普通的方法+模式匹配
def m1(i:Int) = i match {
case 1 => println("一")
case 2 => println("二")
case 3 => println("三")
}
m1(2)
//todo 实现2:使用偏函数实现
val pf2:PartialFunction[Int,String] ={
case 1 => "一"
case 2 => "二"
case 3 => "三"
}
println(pf2(1))
}
}
正则类
- 又叫做规则表达式 用于字符串内容的匹配
- RegEx类
- 直接new
- String.r —>RegEx类
import scala.util.matching.Regex
object RegexTest {
def main(args: Array[String]): Unit = {
val email1 = "miao@qq.com"
val email2 = "liu@com"
var regex:Regex=""".+@.+\\..+""".r
println(regex.findAllMatchIn(email1).toList)
}
}
scala异常处理
- 捕获异常 try catch
- 抛异常throws
object ExceptionCase01 {
def main(args: Array[String]): Unit = {
//todo IDEA快捷键 ctrl +alt +t
try {
//捕获的业务代码 有可能出错的地方
val z = 4 / 0
} catch {
//处理异常地方
// case e:Exception => println(e.getMessage)
case e1:ArithmeticException => println("数值异常"+e1.getMessage)
case e2:NullPointerException => println("空指针异常")
} finally {
//不管是否出错 一定会执行的
println("一定执行")
}
}
}
四、Actor并发编程
Java并发编程
- 模型:基于多线程+共享数据
- 线程越多,并发能力越强
- 不容易维护,容易死锁
scala中并发编程
- 基于时间+消息发送和接受模型
- actor越多并发能力越强
- 不共享数据
import scala.actors.Actor
class Actor03 extends Actor {
override def act(): Unit = {
// todo 在actor内部 使用receive方法进行消息接收 配合模式匹配处理消息
// 如何实现不间断处理消息 使用while true 包装receive方法
while (true) {
receive {
case "hello" => println("欢迎光临")
case "bye" => println("下次再见")
case "stop" => System.exit(0)
}
}
}
}
object Actor03 {
def main(args: Array[String]): Unit = {
val actor03 = new Actor03()
actor03.start()
// 演示 自己给自己发消息
actor03 ! "hello"
actor03 ! "bye"
actor03 ! "stop"
}
}
使用react接收处理消息
- 使用while+receive效率不高 不能复用线程
- 针对react方法 使用loop控制外层循环
import scala.actors.Actor
class Actor04 extends Actor {
override def act(): Unit = {
loop {
react {
case "hello" => println("欢迎光临")
case "bye" => println("下次再见")
case "stop" => System.exit(0)
}
}
}
}
object Actor04 {
def main(args: Array[String]): Unit = {
val actor04 = new Actor04()
actor04.start()
// 演示 自己给自己发消息
actor04 ! "hello"
actor04 ! "bye"
actor04 ! "stop"
}
}
Actor的三种消息发送方式
import scala.actors.{Actor, Future}
case class AsyncMessage(id: Int, msg: String)
case class SyncMessage(id: Int, msg: String)
case class Reply(msg: String)
class Actor05 extends Actor {
override def act(): Unit = {
loop {
react {
case AsyncMessage(id, msg) => {
println(s"id:${id}-->msg:${msg}")
// todo 返回消息
sender ! Reply("已收到,并回复消息01!")
}
case SyncMessage(id, msg) => {
println(s"id:${id}-->msg:${msg}")
}
}
}
}
}
//! 异步消息,没有返回值
//!! 异步信息,有返回值
//!? 同步消息 等待返回值
object Actor05 {
def main(args: Array[String]): Unit = {
val actor05 = new Actor05()
actor05.start()
//todo 1、异步消息 没有返回值 !
actor05 ! AsyncMessage(1, "这是一条异步消息,不需要返回值!")
//todo 3、同步消息 等待返回值 !? 阻塞动作
actor05 !? SyncMessage(3, "这是同步阻塞消息!")
//todo 2、异步消息 需要返回值 !! Future[Any]
//针对异步消息的返回值 其立即受到的一个未来的状态 叫做future 至于最终的返回结果 在对方处理完之后 会把最终的结果放在future中
//可以从future中提取最终的结果
val future: Future[Any] = actor05 !! AsyncMessage(2, "这是一条异步消息需要返回值")
val result = future.apply().asInstanceOf[Reply]
println(result)
}
}
Actor并发版本–Wordcount
- 初始版本
import scala.actors.Actor
import scala.io.Source
//定义个样例类 用于封装提交的任务
case class SubmitTask(filePath: String)
class Task extends Actor {
override def act(): Unit = {
loop {
react {
//接收的task任务 处理数据 进行wc
case SubmitTask(filePath) => {
//1、从文件中读取数据 并且读取的数据转换为String 底层是全文读
val content: String = Source.fromFile(filePath).mkString
//2、将读取的内容根据换行符切割成为一行行内容 \\r\\n
val lines: Array[String] = content.split("\\r\\n")
//3、根据分隔符进行单词切割 并扁平化操作
val words: Array[String] = lines.flatMap(line => line.split(" "))
//4、将单词标记1 单词----> (单词,1) 对偶元组,二元组
val wordAndOne: Array[(String, Int)] = words.map(word => (word, 1))
//5、将单词相同的分为一组
val wordGrouped: Map[String, Array[(String, Int)]] = wordAndOne.groupBy(tuple2 => tuple2._1)
//6、todo scala集合中有个方法mapValues 只处理map当做value 处理的结果和之前的key组成一个新的map
val result: Map[String, Int] = wordGrouped.mapValues(v => v.length)
for ((k, v) <- result) println(s"${k}---->${v}")
}
}
}
}
}
object WordCount {
def main(args: Array[String]): Unit = {
//1、待处理数据路径
val filePath = "D:\\\\Study\\\\idea\\\\Scala\\\\files\\\\1.txt"
//2、创建启动TaskActor
val task = new Task
task.start()
//3、把待处理的数据路径(任务) 发送给task处理
task ! SubmitTask(filePath)
}
}
- 最终版本
import scala.actors.{Actor, Future}
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.io.Source
//定义个样例类 用于封装提交的任务
case class SubmitTask(filePath:String)
//定义样例类 用于封装task处理的结果
case class ReplyResult(result: Map[String, Int])
class Task extends Actor {
override def act(): Unit ={
loop{
react{
//接收的task任务 处理数据 进行wc
case SubmitTask(filePath) =>{
//1、从文件中读取数据 并且读取的数据转换为String 底层是全文读
val content: String = Source.fromFile(filePath).mkString
//2、将读取的内容根据换行符切割成为一行行内容 \\r\\n
val lines: Array[String] = content.split("\\r\\n")
//3、根据分隔符进行单词切割 并扁平化操作
val words: Array[String] = lines.flatMap(line => line.split(" "))
//4、将单词标记1 单词----> (单词,1) 对偶元组,二元组
val wordAndOne: Array[(String, Int)] = words.map(word => (word, 1))
//5、将单词相同的分为一组
val wordGrouped: Map[String, Array[(String, Int)]] = wordAndOne.groupBy(tuple2 => tuple2._1)
//6、todo scala集合中有个方法mapValues 只处理map当做value 处理的结果和之前的key组成一个新的map
val result: Map[String, Int] = wordGrouped.mapValues(v => v.length)
// for( (k,v)<- result) println(s"${k}---->${v}")
//todo task处理完的结果进行返回
sender ! ReplyResult(result)
}
}
}
}
}
object WordCount {
def main(args: Array[String]): Unit = {
//创建一个集合 用于保存每个任务Future
val futureSet = new mutable.HashSet[Future[Any]]()
//创建一个集合 用于保存最终的每个task处理结果
val resultList = new ListBuffer[ReplyResult]
//1、待处理数据路径
val files = Array("D:\\\\Study\\\\idea\\\\Scala\\\\files\\\\1.txt","D:\\\\Study\\\\idea\\\\Scala\\\\files\\\\2.txt","D:\\\\Study\\\\idea\\\\Scala\\\\files\\\\3.txt")
//val filePath =D:\\\\Study\\\\idea\\\\Scala\\\\files\\\\1.txt"
//遍历待处理文件文件 分别启动task处理各个文件
for(f <- files){
//2、创建启动TaskActor
val task = new Task
task.start()
//3、把待处理的数据路径(任务) 发送给task处理
//因为需要对最终的结果进行合并 异步有返回值的消息
val future: Future[Any] = task !! SubmitTask(f)
//把future保存至集合中
futureSet += future
}
//判断future是否已经完成有结果 如果有结果 提取结果 future.isSet可以判断结果是否有了
while(futureSet.nonEmpty){
//过滤出已经有结果的future
val completed: mutable.HashSet[Future[Any]] = futureSet.filter(f => f.isSet)
//遍历已经完成的 提取结果
for(c <- completed){
//提取结果
val result: ReplyResult = c.apply().asInstanceOf[ReplyResult]
//将结果添加至resultList
resultList += result
//todo 把提取完结果的future从futureSet给剔除
futureSet.remove(c)
}
}
println(resultList.flatMap(r => r.result)
.groupBy(_._1)
.mapValues(values => values.map(x =>x._2).sum))
}
}
五、泛型及其延伸问题
泛型
-
参数化类型 泛型程序设计(generic programming)
-
scala中泛型使用 泛型通常大小字母表示 理论上26个字母都可以 常见的T A B
泛型方法 格式:
def 方法名[泛型名称](..) = {
//...
}
object TestGen {
def main(args: Array[String]): Unit = {
val a1 = new Array[String](4) //T -->String
val a2 = new Array[Int](2) //T --->Int
def m1(a: Array[Int]) = a(a.length / 2)
// println(m1(Array(11,22,33)))
// println(m1(Array("allen","tom","jerry")))
//定一个同样功能方法 使用泛型定义
def m2[A](a: Array[A]) = a(a.length / 2)
println(m2(Array(11, 22, 33)))
println(m2(Array("allen", "tom", "jerry")))
println(m2(Array(3.14, 6.88, 7.52)))
}
}
- 泛型类
object TestGen1 {
def main(args: Array[String]): Unit = {
val jiafei: Cat1 = Cat1("jiafei")
val tom: Cat2[String] = Cat2("tom")
val jerryMao: Cat2[Int] = Cat2(12)
}
}
case class Cat1(var name:String)
case class Cat2[T](var name:T)
- 上下界 限制泛型类型的边界
[T <: 类型] T <=类型 上界 T类型必须是后面指定的类型及其子类
[T >: 类型] T >=类型 下界 T类型必须是后面值的类型及其父类
如果类既有上界、又有下界。下界写在前面,上界写在后面
泛型延伸问题
- 非变 斜变 逆变
- java支持非变
- scala三个都支持
class Super
class Sub extends Super
class Temp1[T] //非变
class Temp2[+T] //保持一致 继续延伸
class Temp3[-T] //逆转过来 延伸
def main(args: Array[String]): Unit = {
val a:Temp1[Sub] = new Temp1[Sub]
// 编译报错
// 非变
//val b:Temp1[Super] = a
// 协变
val c: Temp2[Sub] = new Temp2[Sub]
val d: Temp2[Super] = c
// 逆变
val e: Temp3[Super] = new Temp3[Super]
val f: Temp3[Sub] = e
}
以上是关于Scala:基础知识03的主要内容,如果未能解决你的问题,请参考以下文章