Spark 常用代码段和Scala的一些笔记
Spark 常用代码段
创建SparkContext
1 | val conf = new SparkConf().setAppName(this.getClass.getCanonicalName.init).setMaster("local[*]") |
创建SparkSession
1 | val spark = SparkSession |
SparkSession从csv读取
1 | val df: DataFrame = spark.read |
SparkSession执行sql样例
1 | // 准备数据 |
SparkSession写入csv
1 | spark.sql("select * from people") |
SparkSession连接jdbc
1 | // jdbc |
创建StreamingContext
1 | // 初始化 |
StreamingContext整合Kafka
1 | object KafkaDStream1 { |
Spark GraphX基本操作
1 | object GraphXExample1 { |
Spark GraphX从文件生成图,生成连通图
1 | object GraphXExample2 { |
Scala基础
List
简介
List代表元素顺序固定的不可变的链表,它是Seq的子类,在Scala编程中经常使用。
List列表有头部和尾部的概念,可以分别使用head和tail方法来获取:
- head返回的是列表第一个元素的值
- tail返回的是除第一个元素外的其它元素构成的新列表
Scala定义了一个空列表对象Nil,定义为List[Nothing]
借助 Nil 可将多个元素用操作符 :: 添加到列表头部,常用来初始化列表;
操作符 ::: 用于拼接两个列表
1 | // 不可变集合 |
快速排序
1 | def quickSort(lst:List[Int]):List[Int]= { |
可变list
1 | import scala.collection.mutable.ListBuffer |
获取元素(使用括号访问(索引值))
添加元素(+=)
追加一个列表(++=)
更改元素(使用括号获取元素,然后进行赋值)
删除元素(-=)
转换为List(toList)
转换为Array(toArray)
1 | // 可变集合 |
Scala Iterator(迭代器)
简介
- Scala Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法。
- 迭代器 it 的两个基本操作是 next 和 hasNext。
- 调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
- 调用 it.hasNext() 用于检测集合中是否还有元素。
1 | val lst = List(1,2,3,4) |
Option的类型
Option有两个子类别,Some和None。当程序回传Some的时候,代表这个函式成功地给了你一个String,而你可以透过get()函数拿到那个String,如果程序返回的是None,则代表没有字符串可以给你。
1 | object Option_Test { |
Map类型
如果要使用可变Map,必须导入scala.collection.mutable.Map
1 | val stringToInt = Map("a" -> 50, "b" -> 60, "c" -> 80) |
可变map
1 | // 可变map |
拉链操作创建map
1 | // 拉链操作创建map |
Array
开始学的时候有些不重视,花招还是挺多的
数组可索引、类型一致、长度不变
1 | val nums: Array[Int] = new Array[Int](10) |
变长数组ArrayBuffffer
1 | // 变长数组 |
多维数组Array.ofDim
通过Array的ofDim方法来定义一个多维的数组,多少行,多少列,都是自己说了算
1 | // 3*4数组 |
元组tuple
元组是不同类型的值的集合,元组中的元素可以是不同的数据类型,元组的元素个数上限是22个
Tuple的访问形式比较特殊。元组的下标从1开始
1 | object Tuple_Test { |
None,Nothing,Null,Nil
Null是所有AnyRef的子类,在scala的类型系统中,AnyRef是Any的子类,同时Any子类的还有AnyVal。对应java值类型的所有类型都是AnyVal的子类。所以Null可以赋值给所有的引用类型(AnyRef),不能赋值给值类型,这个java的语义是相同的。 null是Null的唯一对象
Nothing是所有类型的子类,它没有对象,但是可以定义类型,如果一个类型抛出异常,那这个返回值类型就是Nothing
Nil是一个空的List,定义为List[Nothing],根据List的定义List[+A],所有Nil是所有List[T]的子类。
None是一个object,是Option的子类型
类与对象
了解一下
简介
在Scala中,类并不用声明为public;
Scala源文件中可以包含多个类,所有这些类都具有公有可见性;
val修饰的变量(常量),值不能改变,只提供getter方法,没有setter方法;
var修饰的变量,值可以改变,对外提供getter、setter方法;
如果没有定义构造器,类会有一个默认的无参构造器;
基础类的注意事项
Scala中声明一个字段,必须显示的初始化,然后根据初始化的数据类型自动推断其类型,字段类型可以省略
_ 表示一个占位符,编译器会根据变量的数据类型赋予相应的初始值
使用占位符,变量类型必须指定
_ 对应的默认值:整型默认值0;浮点型默认值0.0;String与引用类型,默认值null; Boolean默认值false
val修饰的变量不能使用占位符
1 | class Person { |
自定义getter setter
1 | class Dog { |
bean属性
JavaBean规范把Java属性定义为一堆getter和setter方法。
类似于Java,当将Scala字段标注为 @BeanProperty时,getFoo和setFoo方法会自动生成。
使用@BeanProperty并不会影响Scala自己自动生成的getter和setter方法。
在使用时需要导入包scala.beans.BeanProperty
构造器
如果没有定义构造器,Scala类中会有一个默认的无参构造器;Scala当中类的构造器分为两种:主构造器和辅助构造器;
主构造器的定义与类的定义交织在一起,将主构造器的参数直接放在类名之后。
当主构造器的参数不用var或val修饰时,参数会生成类的私有val成员。
Scala中,所有的辅助构造器都必须调用另外一个构造器,另外一个构造器可以是辅助构造器,也可以是主构造器。
1 | //主构造器直接定义在类中,其代码不包含在任何方法中 |
对象
单例对象
java中单例模式主要用到了静态,但是scala中没有static关键字,但是scala可以用伴生类于伴生对象来模拟静态,但首先我们需要了解单例对象
Scala中的单例对象具有如下特点:
- 1、创建单例对象不需要使用new关键字
- 2、object中只有无参构造器
- 3、主构造代码块只能执行一次,因为它是单例的
1 | object ObjectDemo { |
伴生类与伴生对象
当单例对象与某个类具有相同的名称时,它被称为这个类的“伴生对象”;
类和它的伴生对象必须存在于同一个文件中,而且可以相互访问私有成员(字段和方法);
个人觉得就是为了弥补没有static关键字的手段
1 | class static_test{ |
object中的apply方法
object 中有一个非常重要的特殊方法 – apply方法;
- apply方法通常定义在伴生对象中,目的是通过伴生类的构造函数功能,来实现伴生对象的构造函数功能;
- 通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,…参数n)时apply方法会被调用;
- 在创建伴生对象或伴生类的对象时,通常不会使用new class/class() 的方式,而是直接使用 class()隐式的调用伴生对象的 apply 方法,这样会让对象创建的更加简洁;
1 | abstract class Animal { |
继承
这个和java差不多,依然是extends
类型检查与转换
要测试某个对象是否属于某个给定的类,可以用isInstanceOf方法。如果测试成功,可以用asInstanceOf方法进行类型转换。
1 | if(p.isInstanceOf[Employee]){ |
如果p指向的是Employee类及其子类的对象,则p.isInstanceOf[Employee]将会成功。
如果p是null,则p.isInstanceOf[Employee]将返回false,且p.asInstanceOf[Employee]将返回null。
如果p不是一个Employee,则p.asInstanceOf[Employee]将抛出异常。
如果想要测试p指向的是一个Employee对象但又不是其子类,可以用:
1 | if(p.getClass == classOf[Employee]) |
不过,与类型检查和转换相比,模式匹配通常是更好的选择
1 | p match{ |
特质trait
作为接口trait
Scala中的trait特质是一种特殊的概念。
首先可以将trait作为接口来使用,此时的trait就与Java中的接口非常类似。
在trait中可以定义抽象方法,与抽象类中的抽象方法一样,只要不给出方法的具体实现即可。
类可以使用extends关键字继承trait。
注意:在Scala中没有implement的概念,无论继承类还是trait特质,统一都是extends。
类继承trait特质后,必须实现其中的抽象方法,实现时可以省略override关键字。
Scala不支持对类进行多继承,但是支持多重继承trait特质,使用with关键字即可。
1 | //定义一个trai特质 |
带有具体实现的特质
- Scala中的trait特质不仅仅可以定义抽象方法,还可以定义具体实现的方法,这时的trait更像是包含了通用工具方法的类。比如,trait中可以包含一些很多类都通用的功能方法,比如打印日志等等,Spark中就使用了trait来定义通用的日志打印方法。
- Scala trait特质中的字段可以是抽象的,也可以是具体的。
特质构造顺序
在Scala中,trait特质也是有构造器的,也就是trait中的不包含在任何方法中的代码。
构造器以如下顺序执行:
- 1、执行父类的构造器;
- 2、执行trait的构造器,多个trait从左到右依次执行;
- 3、构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;
- 4、所有trait构造完毕之后,子类的构造器才执行
1 | class Person2 { println("Person's constructor!") } |
特质继承类
在Scala中,trait特质也可以继承class类,此时这个class类就会成为所有继承此trait的类的父类。
排序Ordered和Ordering
在Java中对象的比较有两个接口,分别是Comparable和Comparator。它们之间的区别在于:
实现Comparable接口的类,重写compareTo()方法后,其对象自身就具有了可比较性; 实现Comparator接口的类,重写了compare()方法后,则提供一个第三方比较器,用于比较两个对象。
在Scala中也引入了以上两种比较方法(Scala.math包下):
Ordered特质混入Java的Comparable接口,它定义了相同类型间的比较方式,但这种内部比较方式是单一的;
1 | trait Ordered[A] extends Any with java.lang.Comparable[A]{......} |
1 | case class Project(tag:String, score:Int) extends Ordered[Project] { |
Ordering特质混入Comparator接口,它是提供第三方比较器,可以自定义多种比较方式,在实际开发中也是使用比较多的,灵活解耦合。
1 | trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable {......} |
1 | object OrderingDemo { |
模式匹配和样例类
模式匹配
模式匹配的基本语法结构:变量 match { case 值 => 代码 }
模式匹配match case中,只要有一个case分支满足并处理了,就不会继续判断下一个case分支了,不需要使用break语句。这点与Java不同,Java的switch case需要用break阻止。如果值为下划线,则代表不满足以上所有情况的时候如何处理。
模式匹配match case最基本的应用,就是对变量的值进行模式匹配。match是表达式,与if表达式一样,是有返回值的。
除此之外,Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配。
匹配类型
Scala的模式匹配还有一个强大的功能,它可以直接匹配类型,而不是值。这一点是Java的switch case做不到的。
匹配类型的语法:变量 match {case 变量 : 类型 => **代码}**,而不是匹配值的“case 值 => 代码”这种语法。
匹配数组、元组、集合
1 | def main(args: Array[String]): Unit = { |
样例类
case class样例类是Scala中特殊的类。当声明样例类时,以下事情会自动发生:
- 主构造函数接收的参数通常不需要显式使用var或val修饰,Scala会自动使用val修饰
- 自动为样例类定义了伴生对象,并提供apply方法,不用new关键字就能够构造出相应的对象
- 将生成toString、equals、hashCode和copy方法,除非显示的给出这些方法的定义
- 继承了Product和Serializable这两个特质,也就是说样例类可序列化和可应用Product的方法
case class是多例的,后面要跟构造参数,case object是单例的。
此外,case class样例类中可以添加方法和字段,并且可用于模式匹配。
1 | class Amount |
Option与模式匹配
1 | object Option_Test { |
函数
基础
1 | // 函数 |
闭包
闭包是在其上下文中引用了自由变量的函数;
闭包引用到函数外面定义的变量,定义这个函数的过程就是将这个自由变量捕获而构成的一个封闭的函数,也可理解
为”把函数外部的一个自由变量关闭进来“。
何为闭包?需满足下面三个条件:
- 1、闭包是一个函数
- 2、函数必须要有返回值
- 3、返回值依赖声明在函数外部的一个或多个变量,用Java的话说,就是返回值和定义的全局变量有关
柯里化
函数编程中,接收多个参数的函数都可以转化为接收单个参数的函数,这个转化过程就叫柯里化(Currying)。
Scala中,柯里化函数的定义形式和普通函数类似,区别在于柯里化函数拥有多组参数列表,每组参数用小括号括起来。
Scala API中很多函数都是柯里化的形式。
1 | // 使用普通的方式 |
偏函数
偏函数(Partial Function)之所以“偏”,原因在于它们并不处理所有可能的输入,而只处理那些能与至少一个 case语句匹配的输入;
在偏函数中只能使用 case 语句,整个函数必须用大括号包围。这与普通的函数字面量不同,普通的函数字面量可以使用大括号,也可以用小括号;
被包裹在大括号中的一组case语句是一个偏函数,是一个并非对所有输入值都有定义的函数;
Scala中的Partial Function是一个trait,其类型为PartialFunction[A,B],表示:接收一个类型为A的参数,返回一个类型为B的结果。
1 | // 1、2、3有对应的输出值,其它输入打印 Other |
隐式转换
隐式转换函数
1 | class Num {} |
泛型
泛型类
1 | //定义一个泛型类 |
泛型函数
1 | object GenericityFunction { |
协变和逆变
Scala的协变和逆变是非常有特色的,完全解决了Java中的泛型的一大缺憾!
举例来说,Java中,如果有Professional是Master的子类,那么Card[Professionnal]是不是Card[Master]的子类?
答案是:不是。因此对于开发程序造成了很多的麻烦。
而Scala中,只要灵活使用协变和逆变,就可以解决Java泛型的问题。
协变定义形式如:trait List[+T] {}
当类型S是类型A的子类型时,则List[S]也可以认为是List[A}的子类型,即List[S]可以泛化为List[A],也就是被参数化,类型的泛化方向与参数类型的方向是一致的,所以称为协变(covariance)。
逆变定义形式如:trait List[-T] {}
当类型S是类型A的子类型,则Queue[A]反过来可以认为是Queue[S}的子类型,也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变(contravariance)。
1 | //大师 |
Spark基础
mapPartitions
与map方法类似,map是对rdd中的每一个元素进行操作,而mapPartitions(foreachPartition)则是对rdd中的每个分区的迭代器进行操作。如果在map过程中需要频繁创建额外的对象(例如将rdd中的数据通过jdbc写入数据库,map需要为每个元素创建一个链接而mapPartition为每个partition创建一个链接),则mapPartitions效率比map高的多。
1 | /** |
本文链接: http://woaixiaoyuyu.github.io/2021/07/03/Spark%E5%B8%B8%E7%94%A8%E4%BB%A3%E7%A0%81%E6%AE%B5/
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!