spark--用六种方法实现自定义排序

在实际开发中,我们经常需要自己定义排序规则对数据进行排序。

例如这有一组数据

val users = Array("li 30 99","zhao 29 999","zhang 28 98","wang 28 99")

数据含义为:名字,年龄,颜值

现在需要对这组数据进行排序,排序规则是,按颜值从高到低进行排序,颜值相同的话,再按年龄从低到高排序。

下面用六种方法进行实现:

自定义一个类,用来定义排序规则,然后切分整理数据,将数据以排序规则类的对象的形式保存

代码:

package XXX

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[4]")
    val sc = new SparkContext(conf)

    //排序规则,首先按照颜值从高到低排序,颜值相同,再按年龄从低到高排序
    val users = Array("li 30 99","zhao 29 999","zhang 28 98","wang 28 99")

    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)
    })

//数据已经保存到了对象中,所以就按照对象中定义的排序方法进行排序
//返回一个RDD,里面保存的是User对象
    val sorted: RDD[User] = userRDD.sortBy(u => u)

    val r: Array[User] = sorted.collect()

    println(r.toBuffer)

    sc.stop()

  }

}

//需要让该类继承Ordered,才可以用来排序
//还要让该类继承Serializable,进行序列化,这样才可以将对象保存起来
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,$age,$fv"

}

也要先定义一个类,类中定义排序规则,但是与第一种方法不同的是,这次切分整理数据的时候将数据保存在元组中,然后排序的时候将元组中的数据传入该对象。
这种方法在sortedBy中传入的是一个排序规则,不会改变数据的格式,只会改变顺序

代码:

package XXX

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[4]")
    val sc = new SparkContext(conf)

    val users = Array("li 30 99","zhao 29 999","zhang 28 98","wang 28 99")

    val lines: RDD[String] = sc.parallelize(users)

    val userRDD1: 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)] = userRDD1.sortBy(tp => new Boy(tp._1,tp._2,tp._3))

    println(sorted.collect().toBuffer)

    sc.stop()

  }

}

class Boy(val name:String,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)
    }
  }

  override def toString: String = s"$name,$age,$fv"
}

我们可以使用Scala中的case class,这种类是已经实现了序列化
而且可以直接用类名创建对象

代码:

package XXX

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[4]")
    val sc = new SparkContext(conf)

    val users = Array("li 30 99","zhao 29 999","zhang 28 98","wang 28 99")

    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方法的源码:

 /**
   * Return this RDD sorted by the given key function.
   */
  def sortBy[K](
      f: (T) => K,
      ascending: Boolean = true,
      numPartitions: Int = this.partitions.length)
      (implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T] = withScope {
    this.keyBy[K](f)
        .sortByKey(ascending, numPartitions)
        .values
  }

该方法要使用一个Ordering的隐式转换

所以我们可以定义一个隐式转换,隐式转换中定义排序规则

关于隐式转换的讲解,请参考博客:https://blog.csdn.net/weixin_43866709/article/details/88358235

SortRules.scala

package XXX 

object SortRules {
//定义隐式转换,将XiaoRou转换为Ordering类型
  implicit object OrderingToXianRou 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
      }
    }
  }

}

CustomSort4.scala

package XXX 

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[4]")
    val sc = new SparkContext(conf)

    val users = Array("li 30 99","zhao 29 999","zhang 28 98","wang 28 99")

    val lines: RDD[String] = sc.parallelize(users)

    val tpRDD1: 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.OrderingToXianRou
    val sorted: RDD[(String, Int, Int)] = tpRDD1.sortBy(tp => XianRou(tp._2,tp._3))

    println(sorted.collect().toBuffer)

    sc.stop()


  }

}
//定义类XianRou
case class XianRou(age:Int,fv:Int)

重新定义一个类太麻烦了,对于一些简单的业务逻辑来说,我们可以使用元组的比较规则来进行排序。
元组的比较规则是:先比第一个元素,如果第一个元素相等,再比第二个

代码:

package XXX

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

object CustomSort5 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("CustomSort5").setMaster("local[4]")
    val sc = new SparkContext(conf)

    val users = Array("li 30 99","zhao 29 999","zhang 28 98","wang 28 99")

    val lines: RDD[String] = sc.parallelize(users)

    val topRDD2: 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)] = topRDD2.sortBy(tp => (-tp._3,tp._2))

    println(sorted.collect().toBuffer)

    sc.stop()

  }

}

我们可以使用Ordering中的on方法,不改变元组的形态,就按照元组本来的组成顺序进行排序

代码:

package XXX

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

object CustomSort6 {

  def main(args: Array[String]): Unit = {

    val conf = new SparkConf().setAppName("CustomSort5").setMaster("local[4]")
    val sc = new SparkContext(conf)

    val users = Array("li 30 99","zhao 29 999","zhang 28 98","wang 28 99")

    val lines: RDD[String] = sc.parallelize(users)

    val topRDD2: 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)
    })

    //利用元组的比较规则(先比第一个元素,如果第一个元素相等,再比第二个)进行排序
    //Ordering[(Int,Int)]最终比较规则的样式
    //on[(String,Int,Int)]未比较之前的数据格式
    //(t => (-t._3,t._2))怎样将规则转换成想要比较的格式
    implicit val rules: Ordering[(String, Int, Int)] = Ordering[(Int,Int)].on[(String,Int,Int)](t => (-t._3,t._2))
    val sorted: RDD[(String, Int, Int)] = topRDD2.sortBy(tp => tp)

    println(sorted.collect().toBuffer)

    sc.stop()

  }

}

总结

这就是六种自定义排序的方法,大多数情况下我们使用第五种方法就可以了,而且是最简单的方法。
在面对更加复杂的排序规则时,我们可以定义一个类来编写排序的规则。

猜你喜欢

转载自blog.csdn.net/weixin_43866709/article/details/88777565
今日推荐