一、目标对象添加隐式转换函数的方式实现自定义排序
object MySort{
//为student类添加隐式转换函数
implicit val stuOrdering = new Ordering[student]{
override def compare(x: student, y: student) = {
//自定义排序格式为:默认按年龄升序排序,当年龄相同时,按分数降序排序
if(x.age != y.age){
x.age - y.age
}else{
y.score - x.score
}
}
}
}
/**
* 隐式转换函数实现自定义排序
*/
object CustomSort1{
def main(args: Array[String]): Unit = {
//1.创建配置对象
val conf = new SparkConf().setAppName("sort1").setMaster("local[2]");
//2.创建集群入口类对象
val sc = new SparkContext(conf);
//3.并行化的方式生成rdd
val rdd = sc.parallelize(Array(("tom",24,92),("ko",24,97),("mark",28,88),("jack",18,86)))
//4.对数据进行排序
//引入隐式转换函数
import MySort.stuOrdering
val resRdd = rdd.sortBy(x => student(x._1,x._2.toInt,x._3.toInt))
//5.打印排序后的结果数据
println(resRdd.collect().toBuffer)
}
}
//学生类
case class student(name:String,age:Int,score:Int)
二、目标对象实现Ordered特质的方式实现自定义排序
object CustomSort2{
def main(args: Array[String]): Unit = {
//1.创建配置对象
val conf = new SparkConf().setAppName("sort2").setMaster("local[2]");
//2.创建集群入口类对象
val sc = new SparkContext(conf);
//3.并行化的方式生成rdd
val rdd = sc.parallelize(Array(("tom",24,92),("ko",24,97),("mark",28,88),("jack",18,86)))
//4.对数据进行排序
val resRdd = rdd.sortBy(x => student2(x._1,x._2.toInt,x._3.toInt))
//5.打印排序后的结果数据
println(resRdd.collect().toBuffer)
}
}
case class student2(name:String,age:Int,score:Int) extends Ordered[student2]{
override def compare(that: student2): Int = {
if(this.age != that.age){
this.age - that.age
}else{
that.score - this.score
}
}
}
三、其他序列化
package com.fuge.bigdata.datahub.analysis
import java.io.{DataInput, DataOutput}
import com.fuge.bigdata.tools.common.utils.SparkUtils
import org.apache.hadoop.io.{NullWritable, WritableComparable}
import org.apache.spark.SparkContext
/**
* Created by chen xiang on 18-6-13.
* 一个使用SequenceFile进行存储读取的使用示例
*/
object SequenceFileUsage {
def main(args: Array[String]): Unit = {
require(args.length == 1)
// 构建SparkContext对象,封装过,单独运行,自行修改后定义
val sc = new SparkContext(SparkUtils.getSparkConf("SequenceFileUsage"))
// 获取路径参数
val path = args(0).trim
// 定义测试数据
val studentList = List(Student("01", "abc"), Student("02", "baby"), Student("03", "xiang"))
// 序列化测试数据到RDD,并写入到bos
sc.parallelize(studentList)
.repartition(1)
// 以NullWritable 为key,构建kv结构.SequenceFile需要kv结构才能存储,NullWritable不占存储
.map(NullWritable.get() -> _)
// 压缩参数可选用
.saveAsSequenceFile(s"$path", Option(classOf[GzipCodec]))
// 读取刚才写入的数据
val studentRdd = sc.sequenceFile(s"$path/part-*", classOf[NullWritable], classOf[Student])
.map {
// 读取数据,并且重新赋值对象
case (_, y) => Student(y.id, y.name)
}
.persist()
studentRdd
.foreach(x => println("count: " + x.id + "\t" + x.name))
}
}
case class Student(var id: String, var name: String) extends WritableComparable[Student] {
/**
* 重写无参构造函数,用于反序列化时的反射操作
*/
def this() {
this("", "")
}
/**
* 继承Comparable接口需要实现的方法,用于比较两个对象的大小
*/
override def compareTo(o: Student): Int = {
var cmp = id compareTo o.id
if (cmp == 0) {
cmp = name compareTo o.name
}
cmp
}
/**
* 继承Writable接口需要实现的方法-反序列化读取结果,并且赋值到对象字段
* 注意要和write的顺序一致
*/
override def readFields(in: DataInput): Unit = {
name = in.readUTF()
id = in.readUTF()
println("count: " + "\t id = " + id + "\t name = " + name)
}
/**
* 继承Writable接口需要实现的方法-序列化写操作,将对象字段值写入序列化
* 注意要和readFields的顺序一致
*/
override def write(out: DataOutput): Unit = {
out.writeUTF(id)
out.writeUTF(name)
}
}
补充:
1. 自定义的类需要进行序列化,必须都要实现Writable(或者直接实现Seriable,就不用重写了),一般情况下采用实现WritableComparable的方式,并且实现comparaTo,readFields, write方法,并且提供一个无参构造函数
2. readFields和write方法,里面字段的顺序要保持一致
3. 遇到集合类型,序列化时需要先将集合长度写进去,然后再挨个写集合数据
4. 遇到集合类型,反序列化时需要先读取集合的长度,然后接收数据,如果集合数据类型是自定义类型,还需要先实例化一个无参构造,然后赋值。
5. SequenceFile需要使用KV结构才能调用存储,可以使用一个NullWritable来占位,上诉例子中的K值就是使用的NullWritable进行的
6. sequenceFile序列化后占用的存储空间比较大,有需要的话,可以在存储的时候加上压缩算法,具体使用方式可以见上诉的例子