Spark二次排序学习总结

本文介绍Spark中实现二次排序的方法,并通过Java与Scala代码实例展示如何自定义比较器以完成复杂数据结构的排序。

1. 二次排序

Spark二次排序,即组装一个新的key并在这个key里实现排序接口所定义的方法。

例如一组数据:(点击次数,下单次数,支付次数)
A:(30,35,40)
B:(35,35,40)
C:(30,38,40)
D:(35,35,45)

需要分别对点击次数,下单次数,支付次数做比较。比较完35【点击次数】相等,则要对【下单次数】二次比较,若【下单次数】还是相等,则要对【支付次数再次比较】直到返回正确比较结果。

二次排序即需要自定义key 以及比较方法并返回比较结果。

2.Java代码实现如下:


import java.io.Serializable;

import scala.math.Ordered;

/**
 * 品类二次排序key
 * 
 * 封装你要排序的那几个字段
 * 
 * 实现ordered接口要求的几个方法
 * 
 * 跟其他key相比,如何来判定大于,大于等于,小于,小于等于
 * 
 * 必须实现序列化接口(否则会报错)
 * 
 * @author lxh
 *
 */
public class CategorySortKey implements Ordered<CategorySortKey> ,Serializable{

    private long clickCount;
    private long orderCount;
    private long payCount;





    public CategorySortKey(long clickCount, long orderCount, long payCount) {
        super();
        this.clickCount = clickCount;
        this.orderCount = orderCount;
        this.payCount = payCount;
    }


    /**
     * 大于
     */
    @Override
    public boolean $greater(CategorySortKey other) {

        if(clickCount > other.getClickCount()){
            return true;
        }else if(clickCount == other.getClickCount() &&
                orderCount>other.getOrderCount()){
            return true;
        }else if(clickCount == other.getClickCount() &&
                orderCount == other.getOrderCount() &&
                payCount > other.getPayCount()){
            return true;
        }
        return false;
    }


    /**
     * 大于等于
     */
    @Override
    public boolean $greater$eq(CategorySortKey other) {
        if($greater(other)){
            return true;
        }else if(clickCount == other.getClickCount() &&
                orderCount == other.getOrderCount() &&
                payCount == other.getPayCount()){
            return true;
        }
        return false;
    }

    /**
     * 小于
     */
    @Override
    public boolean $less(CategorySortKey other) {
        if(clickCount < other.getClickCount()){
            return true;
        }else if(clickCount == other.getClickCount() &&
                orderCount < other.getOrderCount()){
            return true;
        }else if(clickCount == other.getClickCount() &&
                orderCount == other.getOrderCount() &&
                payCount < other.getPayCount()){
            return true;
        }
        return false;
    }

    /**
     * 小于等于
     */
    @Override
    public boolean $less$eq(CategorySortKey other) {
        if($less(other)){
            return true;
        }else if(clickCount == other.getClickCount() &&
                orderCount == other.getOrderCount() &&
                payCount == other.getPayCount()){
            return true;
        }
        return false;
    }

    @Override
    public int compare(CategorySortKey other) {
        if(clickCount-other.getClickCount()!=0){
            return (int)(clickCount-other.getClickCount());
        }else if(orderCount - other.getOrderCount()!=0){
            return (int)(orderCount - other.getOrderCount());
        }else if(payCount - other.getPayCount()!=0){
            return (int)(payCount - other.getPayCount());
        }
        return 0;
    }

    @Override
    public int compareTo(CategorySortKey other) {
        if(clickCount-other.getClickCount()!=0){
            return (int)(clickCount-other.getClickCount());
        }else if(orderCount - other.getOrderCount()!=0){
            return (int)(orderCount - other.getOrderCount());
        }else if(payCount - other.getPayCount()!=0){
            return (int)(payCount - other.getPayCount());
        }
        return 0;
    }




    public final long getClickCount() {
        return clickCount;
    }

    public final void setClickCount(long clickCount) {
        this.clickCount = clickCount;
    }

    public final long getOrderCount() {
        return orderCount;
    }

    public final void setOrderCount(long orderCount) {
        this.orderCount = orderCount;
    }

    public final long getPayCount() {
        return payCount;
    }

    public final void setPayCount(long payCount) {
        this.payCount = payCount;
    }

}

3.Scala代码实现:

/**
  * 自定义的key
  * 
 * @author lxh
  *
 */
class SessionSortKey(val clickCount:Int, val orderCount:Int,val payCount:Int) extends Ordered[SessionSortKey] with Serializable{


  def compare(other:SessionSortKey):Int = {
    if(this.clickCount - other.clickCount != 0){
      this.clickCount - other.clickCount
    }else if(this.orderCount - other.orderCount!= 0){
      this.orderCount - other.orderCount
    }else if( this.payCount - other.payCount != 0){
      this.payCount - other.payCount
    }else{
      /**
        * 一定要有else!!!
        */
      0
    }
  }
}

Scala测试类:

import org.apache.spark.{SparkConf, SparkContext}

/**
  * Created by lxh on 2016/8/17.
  */
object SessionSortKeyTest {
  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("sortKeyTest").setMaster("local")

    val sc = new SparkContext(conf)

    val ar = Array(new Tuple2(new SessionSortKey(30,35,40),"1"),
    new Tuple2(new SessionSortKey(35,35,40),"2"),
    new Tuple2(new SessionSortKey(30,38,40),"3"))


    val rdd = sc.parallelize(ar,10)
    val sortedRdd = rdd.sortByKey(false)

    for(tuple <- sortedRdd.collect()){
      println(tuple._2)
    }

    /**
      * 输出结果
      * 2
      * 3
      * 1
      * 正确
      */


  }

}
### Spark 二次排序实现方法 在 Spark 中实现二次排序的核心在于自定义键(Key),并让该键继承 `Ordered` 和 `Serializable` 接口。通过重写 `compare` 方法来指定排序逻辑,最终利用 `sortByKey` 算子完成排序操作。 以下是基于 Scala 的具体实现方式: #### 自定义 Key 类 为了支持多字段排序,需创建一个自定义的 Key 类,并实现 `Ordered` 接口中的 `compare` 方法。此方法决定了排序顺序。 ```scala class SecondarySortKey(val first: Int, val second: Int) extends Ordered[SecondarySortKey] with Serializable { override def compare(that: SecondarySortKey): Int = { if (this.first - that.first != 0) { this.first - that.first // 首先按第一个字段升序排列 } else { this.second - that.second // 如果第一个字段相同,则按第二个字段升序排列 } } override def toString(): String = s"$first,$second" } ``` #### 主程序代码 下面是一个完整的示例代码,展示如何读取文件、映射为 `(Key, Value)` 对、执行排序以及输出结果。 ```scala object SparkSecondarySortExample { def main(args: Array[String]): Unit = { val conf = new SparkConf().setAppName("SparkSecondarySort").setMaster("local[*]") val sc = new SparkContext(conf) // 加载输入数据 val inputRDD = sc.textFile("input/data.txt") // 将每行转换为 (CustomKey, Line) 形式的 RDD val keyValueRDD = inputRDD.map { line => val fields = line.split(",") (new SecondarySortKey(fields(0).toInt, fields(1).toInt), line) } // 按照自定义 Key 进行排序 val sortedRDD = keyValueRDD.sortByKey() // 提取出原始的数据行 val resultRDD = sortedRDD.map(_._2) // 输出结果 resultRDD.collect().foreach(println) sc.stop() } } ``` 以上代码实现了以下功能: - **加载数据**:从本地文件系统中读取数据[^1]。 - **映射处理**:将每一行解析为由自定义 Key 和原字符串组成的元组[^4]。 - **排序操作**:调用 `sortByKey()` 函数按照自定义 Key 定义的规则进行全局排序[^2]。 - **提取结果**:移除辅助的 Key 数据结构,仅保留实际的内容[^3]。 #### 输入与输出样例 假设输入文件 `data.txt` 如下: ``` 1,5,Alice 2,3,Bob 1,7,Charlie 2,1,Dave ``` 运行上述程序后得到的结果将是: ``` 1,5,Alice 1,7,Charlie 2,1,Dave 2,3,Bob ``` 这表明首先依据第一列数值从小到大排序;如果存在相同的值,则进一步比较第二列以决定位置关系。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值