@R星校长
Scala第二天【Scala内容】
主要内容
- Scala 迭代器模式处理数据
- ScalaTrait 特质特性
- Scala 模式匹配
- Scala 偏函数
- Scala 样例类
- Scala 隐式转换
- Scala Actor 通信模型
- Spark WordCount
学习目标
第一节 Scala 迭代器模式 + Trait 特质特性
Scala迭代器模式处理数据
scala 中创建集合需要内存,集合与集合之间的转换时,每次转换生成新的集合时,新的集合也需要内存。如果有一个非常大的初始集合,需要经过多次转换,每次转换都生成一个新的集合,才能得到最终的结果,那么这时,在集合转换过程中内存开销非常大。Scala 迭代器模式处理数据,很好的解决了内存占用大的问题。
Scala 迭代器模式处理数据每次将集合的转换转变成了迭代器之间的转换,迭代器是不需要占用内存存储的,迭代器只是一个指针,指向了最初的原始数据,这样,数据处理过程中内存占用非常小。
迭代器模式处理示例:
1. //非迭代器模式处理,浪费内存
2. val list1 = List[String]("hello java","hello python","hello scala")
3. val list2 = list1.flatMap(one=>{one.split(" ")})
4. val list3 = list2.map(one=>{one+"#"})
5. list3.foreach(println)
6.
7. println("********************")
8.
9. //迭代器模式处理,内存小
10. val list = List[String]("hello java","hello python","hello scala")
11. val iter1 = list.iterator
12. val iter2 = iter1.flatMap(one=>{one.split(" ")})
13. val iter3 = iter2.map(one=>{one+"#"})
14. while(iter3.hasNext){
15. val one = iter3.next()
16. println(one)
17. }
Trait 特质特性
- 概念理解
Scala Trait (特质) 相当于 Java 的接口,实际上它比接口还功能强大。与接口不同的是,它还可以定义属性和方法的实现。
一般情况下 Scala 的类可以继承多个 Trait,从结果来看就是实现了多重继承。第一个关键字使用 extends,之后使用 with。
Trait (特质) 定义的方式与类类似,但它使用的关键字是 trait。 - 举例:trait 中带属性带方法实现
注意:
- 继承的多个 trait 中如果有同名的方法和属性,必须要在类中使用 “override” 重新定义。
- trait 中不可以传参数
1. trait Read {
2. val readType = "Read"
3. val gender = "m"
4. def read(name:String){
5. println(name+" is reading")
6. }
7. }
8.
9. trait Listen {
10. val listenType = "Listen"
11. val gender = "m"
12. def listen(name:String){
13. println(name + " is listenning")
14. }
15. }
16.
17. class Person() extends Read with Listen{
18. override val gender = "f"
19. }
20.
21. object test {
22. def main(args: Array[String]): Unit = {
23. val person = new Person()
24. person.read("zhangsan")
25. person.listen("lisi")
26. println(person.listenType)
27. println(person.readType)
28. println(person.gender)
29.
30. }
31. }
- 举例:trait 中带方法不实现
1. object Lesson_Trait2 {
2. def main(args: Array[String]): Unit = {
3. val p1 = new Point(1,2)
4. val p2 = new Point(1,3)
5. println(p1.isEqule(p2))
6. println(p1.isNotEqule(p2))
7. }
8. }
9.
10. trait Equle{
11. def isEqule(x:Any) :Boolean
12. def isNotEqule(x : Any) = {
13. !isEqule(x)
14. }
15. }
16.
17. class Point(x:Int, y:Int) extends Equle {
18. val xx = x
19. val yy = y
20.
21. def isEqule(p:Any) = {
22. p.isInstanceOf[Point] && p.asInstanceOf[Point].xx==xx
23. }
24.
25. }
第二节 Scala 模式匹配+偏函数
模式匹配
- 概念理解:
Scala 提供了强大的模式匹配机制,应用也非常广泛。
一个模式匹配包含了一系列备选项,每个都开始于关键字 case。
每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。 - 代码及注意点
- 模式匹配不仅可以匹配值还可以匹配类型
- 从上到下顺序匹配,如果匹配到则不再往下匹配
- 都匹配不上时,会匹配到 case _ ,相当于 default
- match 的最外面的 “{ }” 可以去掉看成一个语句
1. object Lesson_Match {
2. def main(args: Array[String]): Unit = {
3. val tuple = Tuple6(1,2,3f,4,"abc",55d)
4. val tupleIterator = tuple.productIterator
5. while(tupleIterator.hasNext){
6. matchTest(tupleIterator.next())
7. }
8.
9. }
10. /**
11. * 注意点:
12. * 1.模式匹配不仅可以匹配值,还可以匹配类型
13. * 2.模式匹配中,如果匹配到对应的类型或值,就不再继续往下匹配
14. * 3.模式匹配中,都匹配不上时,会匹配到 case _ ,相当于default
15. */
16. def matchTest(x:Any) ={
17. x match {
18. case x:Int=> println("type is Int")
19. case 1 => println("result is 1")
20. case 2 => println("result is 2")
21. case 3=> println("result is 3")
22. case 4 => println("result is 4")
23. case x:String => println("type is String")
24. // case x :Double => println("type is Double")
25. case _ => println("no match")
26. }
27. }
28.
29. }
偏函数
如果一个方法中没有 match 只有 case,这个函数可以定义成 PartialFunction 偏函数。偏函数定义时,不能使用括号传参,默认定义 PartialFunction 中传入一个值,匹配上了对应的 case,返回一个值,只能匹配同种类型。
1. /**
2. * 一个函数中只有case 没有match ,可以定义成PartailFunction 偏函数
3. */
4. object Lesson_PartialFunction {
5. def MyTest : PartialFunction[String,String] = {
6. case "scala" =>{"scala"}
7. case "hello"=>{"hello"}
8. case _=> {"no match ..."}
9. }
10. def main(args: Array[String]): Unit = {
11. println(MyTest("scala"))
12. }
13. }
第三节 Scala样例类+隐式转换
样例类(case classes)
- 概念理解
使用了 case 关键字的类定义就是样例类 (case classes),样例类是种特殊的类。实现了类构造参数的 getter 方法(构造参数默认被声明为 val),当构造参数是声明为 var 类型的,它将帮你实现 setter 和 getter 方法。
- 样例类默认帮你实现了 toString , equals , copy 和 hashCode 等方法。
- 样例类可以 new, 也可以不用 new
- 例子:结合模式匹配的代码
1. case class Person1(name:String,age:Int)
2.
3. object Lesson_CaseClass {
4. def main(args: Array[String]): Unit = {
5. val p1 = new Person1("zhangsan",10)
6. val p2 = Person1("lisi",20)
7. val p3 = Person1("wangwu",30)
8.
9. val list = List(p1,p2,p3)
10. list.foreach { x => {
11. x match {
12. case Person1("zhangsan",10) => println("zhangsan")
13. case Person1("lisi",20) => println("lisi")
14. case _ => println("no match")
15. }
16. } }
17.
18. }
19. }
隐式转换
隐式转换是在 Scala 编译器进行类型匹配时,如果找不到合适的类型,那么隐式转换会让编译器在作用范围内自动推导出来合适的类型。
- 隐式值与隐式参数
隐式值是指在定义参数时前面加上 implicit。隐式参数是指在定义方法时,方法中的部分参数是由 implicit 修饰【必须使用柯里化的方式,将隐式参数写在后面的括号中】。隐式转换作用就是:当调用方法时,不必手动传入方法中的隐式参数,Scala 会自动在作用域范围内寻找隐式值自动传入。
隐式值和隐式参数注意:
1). 同类型的参数的隐式值只能在作用域内出现一次,同一个作用域内不能定义多个类型一样的隐式值。
2). implicit 关键字必须放在隐式参数定义的开头
3). 一个方法只有一个参数是隐式转换参数时,那么可以直接定义 implicit 关键字修饰的参数,调用时直接创建类型不传入参数即可。
4). 一个方法如果有多个参数,要实现部分参数的隐式转换,必须使用柯里化这种方式,隐式关键字出现在后面,只能出现一次
1. object Lesson_ImplicitValue {
2.
3. def Student(age:Int)(implicit name:String,i:Int)= {
4. println( s"student :$name ,age = $age ,score = $i")
5. }
6. def Teacher(implicit name:String) ={
7. println(s"teacher name is = $name")
8. }
9.
10. def main(args: Array[String]): Unit = {
11. implicit val zs = "zhangsan"
12. implicit val sr = 100
13.
14. Student(18)
15. Teacher
16. }
17. }
- 隐式转换函数
隐式转换函数是使用关键字 implicit 修饰的方法。当 Scala 运行时,假设如果 A 类型变量调用了 method() 这个方法,发现 A 类型的变量没有 method() 方法,而 B 类型有此 method() 方法,会在作用域中寻找有没有隐式转换函数将 A 类型转换成 B 类型,如果有隐式转换函数,那么 A 类型就可以调用 method() 这个方法。
隐式转换函数注意:隐式转换函数只与函数的参数类型和返回类型有关,与函数名称无关,所以作用域内不能有相同的参数类型和返回类型的不同名称隐式转换函数。
1. class Animal(name:String){
2. def canFly(): Unit ={
3. println(s"$name can fly...")
4. }
5. }
6. class Rabbit(xname:String){
7. val name = xname
8. }
9. object Lesson_ImplicitFunction {
10.
11. implicit def rabbitToAnimal(rabbit:Rabbit):Animal = {
12. new Animal(rabbit.name)
13. }
14.
15. def main(args: Array[String]): Unit = {
16. val rabbit = new Rabbit("RABBIT")
17. rabbit.canFly()
18. }
19. }
- 隐式类
使用 implicit 关键字修饰的类就是隐式类。若一个变量 A 没有某些方法或者某些变量时,而这个变量 A 可以调用某些方法或者某些变量时,可以定义一个隐式类,隐式类中定义这些方法或者变量,隐式类中传入 A 即可。
隐式类注意:
1).隐式类必须定义在类,包对象,伴生对象中。
2).隐式类的构造必须只有一个参数,同一个类,包对象,伴生对象中不能出现同类型构造的隐式类。
1. class Rabbit(s:String){
2. val name = s
3. }
4.
5. object Lesson_ImplicitClass {
6.
7. implicit class Animal(rabbit:Rabbit){
8. val tp = "Animal"
9. def canFly() ={
10. println(rabbit.name +" can fly...")
11. }
12. }
13.
14. def main(args: Array[String]): Unit = {
15. val rabbit = new Rabbit("rabbit")
16. rabbit.canFly()
17. println(rabbit.tp)
18. }
19. }
第四节 Scala Actor 通信模型
- 概念理解
- Java 中的并发编程
Java 中的并发编程是基于共享数据和加锁的一种机制,即会有一个共享的数据,然后有若干个线程去访问这个共享的数据(主要是对这个共享的数据进行修改),同时 Java 利用加锁的机制(即 synchronized)来确保同一时间只有一个线程对我们的共享数据进行访问,进而保证共享数据的一致性。Java 中的并发编程存在资源争夺和死锁等多种问题,因此程序越大问题越麻烦。 - Scala 中的并发编程
Scala 中的并发编程思想与 Java 中的并发编程思想完全不一样,Scala 中的 Actor 是一种不共享数据,依赖于消息传递的一种并发编程模式, 避免了死锁、资源争夺等情况。在具体实现的过程中,Scala 中的 Actor 会不断的循环自己的邮箱,并通过 receive 偏函数进行消息的模式匹配并进行相应的处理。
如果 Actor A 和 Actor B 要相互沟通的话,首先 A 要给 B 传递一个消息,B 会有一个收件箱,然后 B 会不断的循环自己的收件箱, 若看见 A 发过来的消息,B 就会解析 A 的消息并执行,处理完之后就有可能将处理的结果通过邮件的方式发送给 A。
Actor Model 是用来编写并行计算或分布式系统的高层次抽象(类似 java 中的Thread)让程序员不必为多线程模式下共享锁而烦恼,被用在 Erlang 语言上, 高可用性 99.9999999 % 一年只有 31ms 宕机 Actors 将状态和行为封装在一个轻量的进程/线程中,但是不和其他 Actors 分享状态,每个 Actors 有自己的世界观,当需要和其他 Actors 交互时,通过发送事件和消息,发送是异步的,非堵塞的 (fire-andforget),发送消息后不必等另外 Actors 回复,也不必暂停,每个 Actors 有自己的消息队列,进来的消息按先来后到排列,这就有很好的并发策略和可伸缩性,可以建立性能很好的事件驱动系统。
Actor 的特征: - ActorModel 是消息传递模型,基本特征就是消息传递
- 消息发送是异步的,非阻塞的
- 消息一旦发送成功,不能修改
- Actor 之间传递时,自己决定决定去检查消息,而不是一直等待,是异步非阻塞的
什么是 Akka
Akka 是一个用 Scala 编写的库,用于简化编写容错的、高可伸缩性的 Java 和Scala 的 Actor 模型应用,底层实现就是 Actor , Akka 是一个开发库和运行环境,可以用于构建高并发、分布式、可容错、事件驱动的基于 JVM 的应用。使构建高并发的分布式应用更加容易。
spark1.6 版本之前,spark 分布式节点之间的消息传递使用的就是 Akka,底层也就是 actor 实现的。1.6 之后使用的 netty 传输。
- 例:Actor简单例子发送接收消息
1. import scala.actors.Actor
2.
3. class myActor extends Actor{
4.
5. def act(){
6. while(true){
7. receive {
8. case x:String => println("get String ="+ x)
9. case x:Int => println("get Int")
10. case _ => println("get default")
11. }
12. }
13. }
14. }
15.
16. object Lesson_Actor {
17. def main(args: Array[String]): Unit = {
18.
19. //创建actor的消息接收和传递
20. val actor =new myActor()
21. //启动
22. actor.start()
23. //发送消息写法
24. actor ! "i love you !"
25.
26. }
27. }
- 例:Actor 与 Actor 之间通信
1. case class Message(actor:Actor,msg:Any)
2.
3. class Actor1 extends Actor{
4. def act(){
5. while(true){
6. receive{
7. case msg :Message => {
8. println("i sava msg! = "+ msg.msg)
9.
10. msg.actor!"i love you too !"
11. }
12. case msg :String => println(msg)
13. case _ => println("default msg!")
14. }
15. }
16. }
17. }
18.
19. class Actor2(actor :Actor) extends Actor{
20. actor ! Message(this,"i love you !")
21. def act(){
22. while(true){
23. receive{
24. case msg :String => {
25. if(msg.equals("i love you too !")){
26. println(msg)
27. actor! "could we have a date !"
28. }
29. }
30. case _ => println("default msg!")
31. }
32. }
33. }
34. }
35.
36. object Lesson_Actor2 {
37. def main(args: Array[String]): Unit = {
38. val actor1 = new Actor1()
39. actor1.start()
40. val actor2 = new Actor2(actor1)
41. actor2.start()
42. }
43. }
第五节 WordCount
- 创建 Maven 项目导入 pom.xml 文件
安装 Maven 仓库管理工具,版本要求是 3.2 版本以上。新建 Maven 项目,配置 pom.xml。导入必要的包。 - Spark-Scala 版本的 WordCount
1. val conf = new SparkConf()
2. conf.setMaster("local")
3. conf.setAppName("scala-wc")
4. val sc = new SparkContext(conf)
5. val lines = sc.textFile("./data/words")
6. val words = lines.flatMap(line=>{line.split(" ")})
7. val pairWords = words.map(word=>{new Tuple2(word,1)})
8. val result = pairWords.reduceByKey((v1:Int,v2:Int)=>{v1+v2})
9. result.foreach(println)
- Spark-Java 版本的 WordCount
1. SparkConf conf = new SparkConf();
2. conf.setMaster("local");
3. conf.setAppName("java-wc");
4. JavaSparkContext sc = new JavaSparkContext(conf);
5. JavaRDD<String> lines = sc.textFile("./data/words");
6. JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
7. @Override
8. public Iterator<String> call(String s) throws Exception {
9. String[] split = s.split(" ");
10. return Arrays.asList(split).iterator();
11. }
12. });
13. JavaPairRDD<String, Integer> pairWords = words.mapToPair(new PairFunction<String, String, Integer>() {
14. @Override
15. public Tuple2<String, Integer> call(String word) throws Exception {
16. return new Tuple2<>(word, 1);
17. }
18. });
19. JavaPairRDD<String, Integer> result = pairWords.reduceByKey(new Function2<Integer, Integer, Integer>() {
20. @Override
21. public Integer call(Integer v1, Integer v2) throws Exception {
22. return v1 + v2;
23. }
24. });
25. result.foreach(new VoidFunction<Tuple2<String, Integer>>() {
26. @Override
27. public void call(Tuple2<String, Integer> tuple2) throws Exception {
28. System.out.println(tuple2);
29. }
30. });
31. sc.stop();
本节作业
- 掌握Scala迭代器处理数据模式
- 掌握Trait特质特性
- 掌握Scala 模式匹配
- 掌握Scala隐式转换
- 了解Actor通信模型
- 了解Spark WordCount
- 所有代码至少敲一遍