Scala比较器
两个特质
Scala提供两个特质(trait)Ordered与Ordering用于比较。其中,Ordered混入(mix)Java的Comparable接口,而Ordering则混入Comparator接口。众所周知,在Java中
- 实现Comparable接口的类,其对象具有了可比较性;
- 实现comparator接口的类,则提供一个外部比较器,用于比较两个对象
Ordered与Ordering的区别与之相类似:
- Ordered特质定义了相同类型间的比较方式,但这种内部比较方式是单一的;
- Ordered则是提供比较器模板,可以自定义多种比较方式。
实例
在项目中,我们常常会遇到排序(或比较)需求,比如:对一个Person类
case class Person(name: String, age: Int) {
override def toString = {
"name: " + name + ", age: " + age
}
}
对于Person类,如何做让其对象具有可比较性呢?我们可使用Ordered对象的函数orderingToOrdered做隐式转换,但还需要组织一个Ordering[Person]的隐式参数:
implicit object PersonOrdering extends Ordering[Person] {
override def compare(p1: Person, p2: Person): Int = {
p1.name == p2.name match {
case false => -p1.name.compareTo(p2.name)
case _ => p1.age - p2.age
}
}
}
class Person(val name: String, val age: Int, val fv: Int) extends Ordered[Person] {
override def compare(that:Person): Int = {
if(this.name == that.name) {
this.age - that.age
} else {
-(this.age - that.age)
}
}
val p1 = new Person("rain", 13)
val p2 = new Person("rain", 14)
Collection Sort
在实际项目中,我们常常需要对集合进行排序。回到开篇的问题——如何对Person类的集合做指定排序呢?下面用List集合作为demo,探讨在scala集合排序。首先,我们来看看List的sort函数:
// scala.collection.SeqLike
//若使用sortWith,则需要定义返回值为Boolean的比较函数:
def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)
//若使用sortBy,也需要指定Ordering隐式参数
def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr = sorted(ord on f)
//若调用sorted函数做排序,则需要指定Ordering隐式参数
def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
...
}
Spark自定义排序案例
案例一 自定义类
使用自定义类的时候注意下面三点:
- 继承Ordered类,重写compare方法
- 序列化(因为,在进行shuffle 的时候,需要经过网络传输,所以实现序列化接口)
- 重写toString(可选,主要为了可以展示数据)
ackage cn.edu360.day5
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object CustomSort1 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("CustomSort1").setMaster("local[*]")
val sc = new SparkContext(conf)
//排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
val users= Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 98", "laoyang 28 99")
//将Driver端的数据并行化变成RDD
val lines: RDD[String] = sc.parallelize(users)
//切分整理数据
val userRDD: RDD[User] = lines.map(line => {
val fields = line.split(" ")
val name = fields(0)
val age = fields(1).toInt
val fv = fields(2).toInt
//(name, age, fv)
new User(name, age, fv)
})
//不满足要求
//tpRDD.sortBy(tp => tp._3, false)
//将RDD里面装的User类型的数据进行排序
val sorted: RDD[User] = userRDD.sortBy(u => u)
val r = sorted.collect()
println(r.toBuffer)
sc.stop()
}
}
class User(val name: String, val age: Int, val fv: Int) extends Ordered[User] with Serializable {
override def compare(that: User): Int = {
if(this.fv == that.fv) {
this.age - that.age
} else {
-(this.fv - that.fv)
}
}
override def toString: String = s"name: $name, age: $age, fv: $fv"
}
案例二
//定义一个类作为排序规则,由该类继承排序器,在sortby的时候将颜值和年龄传入该类,不会改变数据的格式,只会改变顺序。
package cn.edu360.day5
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object CustomSort2 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("CustomSort2").setMaster("local[*]")
val sc = new SparkContext(conf)
//排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
val users= Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 98", "laoyang 28 99")
//将Driver端的数据并行化变成RDD
val lines: RDD[String] = sc.parallelize(users)
//切分整理数据
val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
val fields = line.split(" ")
val name = fields(0)
val age = fields(1).toInt
val fv = fields(2).toInt
(name, age, fv)
})
//排序(传入了一个排序规则,不会改变数据的格式,只会改变顺序)
val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => new Boy(tp._2, tp._3))
println(sorted.collect().toBuffer)
sc.stop()
}
}
//定义一个类作为排序规则,在sortby的时候将颜值和年龄传入该类
class Boy(val age: Int, val fv: Int) extends Ordered[Boy] with Serializable {
override def compare(that: Boy): Int = {
if(this.fv == that.fv) {
this.age - that.age
} else {
-(this.fv - that.fv)
}
}
}
案例三:样例类
推荐使用case class的原因,主要是因为
1.case class默认实现序列化
2.自动重写了toString
3.不需要new,case class是多例的
package cn.edu360.day5
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object CustomSort3 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("CustomSort3").setMaster("local[*]")
val sc = new SparkContext(conf)
//排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
val users= Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 98", "laoyang 28 99")
//将Driver端的数据并行化变成RDD
val lines: RDD[String] = sc.parallelize(users)
//切分整理数据
val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
val fields = line.split(" ")
val name = fields(0)
val age = fields(1).toInt
val fv = fields(2).toInt
(name, age, fv)
})
//排序(传入了一个排序规则,不会改变数据的格式,只会改变顺序)
val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => Man(tp._2, tp._3))
println(sorted.collect().toBuffer)
sc.stop()
}
}
case class Man(age: Int, fv: Int) extends Ordered[Man] {
override def compare(that: Man): Int = {
if(this.fv == that.fv) {
this.age - that.age
} else {
-(this.fv - that.fv)
}
}
}
案例四:隐式转换
定义一个类,在调用sortby的时候,将比较对象的参数传入,然后再定义一个隐式转化,将该类隐式地转化为继承ordered的类
不改动原本的类
package cn.edu360.day5
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object CustomSort4 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("CustomSort4").setMaster("local[*]")
val sc = new SparkContext(conf)
//排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
val users= Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 98", "laoyang 28 99")
//将Driver端的数据并行化变成RDD
val lines: RDD[String] = sc.parallelize(users)
//切分整理数据
val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
val fields = line.split(" ")
val name = fields(0)
val age = fields(1).toInt
val fv = fields(2).toInt
(name, age, fv)
})
//排序(传入了一个排序规则,不会改变数据的格式,只会改变顺序)
import SortRules.OrderingXiaoRou
val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => XianRou(tp._2, tp._3))
println(sorted.collect().toBuffer)
sc.stop()
}
}
case class XianRou(age: Int, fv: Int)
package cn.edu360.day5
/**
* Created by zx on 2017/10/10.
*/
object SortRules {
implicit object OrderingXiaoRou extends Ordering[XianRou] {
override def compare(x: XianRou, y: XianRou): Int = {
if(x.fv == y.fv) {
x.age - y.age
} else {
y.fv - x.fv
}
}
}
}
案例五:利用本身属性比较
sortBy()可以通过参数的位置与正负,来改变比较规则
package cn.edu360.day5
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* Created by zx on 2017/10/10.
*/
object CustomSort5 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("CustomSort5").setMaster("local[*]")
val sc = new SparkContext(conf)
//排序规则:首先按照颜值的降序,如果颜值相等,再按照年龄的升序
val users= Array("laoduan 30 99", "laozhao 29 9999", "laozhang 28 98", "laoyang 28 99")
//将Driver端的数据并行化变成RDD
val lines: RDD[String] = sc.parallelize(users)
//切分整理数据
val tpRDD: RDD[(String, Int, Int)] = lines.map(line => {
val fields = line.split(" ")
val name = fields(0)
val age = fields(1).toInt
val fv = fields(2).toInt
(name, age, fv)
})
//充分利用元组的比较规则,元组的比较规则:先比第一,相等再比第二个
val sorted: RDD[(String, Int, Int)] = tpRDD.sortBy(tp => (-tp._3, tp._2))
println(sorted.collect().toBuffer)
sc.stop()
}
}
JDBC RDD
JDBC介绍
JDBC API 允许用户访问任何形式的表格数据,尤其是存储在关系数据库中的数据。
JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的。
JDBC API 允许用户访问任何形式的表格数据,尤其是存储在关系数据库中的数据。
执行流程:
- 连接数据源,如:数据库。
- 为数据库传递查询和更新指令。
- 处理数据库响应并返回的结果
代码:
package cn.edu360.day5
import java.sql.{Connection, DriverManager}
import org.apache.spark.rdd.{JdbcRDD, RDD}
import org.apache.spark.{SparkConf, SparkContext}
object JdbcRddDemo {
val getConn = () => {
DriverManager.getConnection("jdbc:mysql://localhost:3306/bigdata?characterEncoding=UTF-8", "root", "123568")
}
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("JdbcRddDemo").setMaster("local[*]")
val sc = new SparkContext(conf)
//创建RDD,这个RDD会记录以后从MySQL中读数据
//new 了RDD,里面没有真正要计算的数据,而是告诉这个RDD,以后触发Action时到哪里读取数据
val jdbcRDD: RDD[(Int, String, Int)] = new JdbcRDD(
sc, //传入SparkContext
getConn, //建立连接
"SELECT * FROM logs WHERE id >= ? AND id < ?", //sql语句
1, //范围下界 ,指定条件的时候使用 ,填充问号
5, //范围上界
2, //分区数量
rs => { //该参数是函数,输入是ResultSet,返回是任意类型,表示结果集
val id = rs.getInt(1)
val name = rs.getString(2)
val age = rs.getInt(3)
(id, name, age)
}
)
//
//触发Action
val r = jdbcRDD.collect()
println(r.toBuffer)
sc.stop()
}
}