RDD排序算子有sortBy和sortByKey两个算作,sortBy算子可以自定义排序规则,而sortByKey只能对Key使用Scala或Spark默认支持的排序规则,如果Scala或Spark不支持排序规则的话,需要使用sortBy自己实现排序规则!


sortByKey的核心实现代码:

class OrderedRDDFunctions extends Logging with Serializable {
	
  private val ordering = implicitly[Ordering[K]] //核心代码1

  def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length): RDD[(K, V)] = self.withScope
  {
    val part = new RangePartitioner(numPartitions, self, ascending)
    new ShuffledRDD[K, V, V](self, part).setKeyOrdering(if (ascending) ordering else ordering.reverse)//核心代码2
  }
   //省略其余代码
}



以上代码我们可以看出,

sortByKey是使用一个ordering隐式值进行排序的,所以说只要当前作用域存在Ordering[K]值就可以排序,但是由于 sortByKey不支持传入隐式值,所以只能使用scala和spark系统默认的隐式值,因此支持部分Key的排序!


接下来看一下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
  }



由源码我们容易看出,sortBy实现底层使用的还是sortByKey,sortBy只不过是传入了一个选key的函数,并将排序规则隐式值传入spark隐式转换系统,可以在排序时候找到自定义Key的Ordering[K]。


接下来实战演示一下:

1、实战一下Scala支持的排序类型Int:

def getRandomList(n: Int): List[Int] = {

    var result: List[Int] = Nil

    while (result.length < n) {
      result = Random.nextInt(1000) :: result
    }

    result
  }



以上代码是生成一个n个大小的List序列,供我们生成RDD使用的辅助方法。

scala> val nums = sc.parallelize(getRandomList(10))
res41: Array[Int] = Array(246, 157, 15, 488, 212, 513, 293, 224, 373, 242)
scala> nums.sortBy(x=>x).collect//直接对Scala支持的排序类型直接使用sortBy排序
res45: Array[Int] = Array(15, 157, 212, 224, 242, 246, 293, 373, 488, 513)

直接对Scala支持的排序类型直接使用sortBy排序,不需要我们实现一个隐式值Ordering[Int],因为这个Scala隐式系统已经存在该值,在scala.math.Ordering中,实现代码如下:

trait IntOrdering extends Ordering[Int] {
    def compare(x: Int, y: Int) =
      if (x < y) -1
      else if (x == y) 0
      else 1
  }
  implicit object Int extends IntOrdering

其他Long,Short等类型同理!


2、接下来对1中nums转成元组KV形式之后排序:

对于元组排序,scala和spark提供了一些元组的Ordering隐式值,在scala.math.Ordering中。接下来我们讲解一下二元组的隐式值:

implicit def Tuple2[T1, T2](implicit ord1: Ordering[T1], ord2: Ordering[T2]): Ordering[(T1, T2)] =
    new Ordering[(T1, T2)]{
      def compare(x: (T1, T2), y: (T1, T2)): Int = {
        val compare1 = ord1.compare(x._1, y._1)
        if (compare1 != 0) return compare1
        val compare2 = ord2.compare(x._2, y._2)
        if (compare2 != 0) return compare2
        0
      }
    }

这个默认的二元组排序规则是先根据第一个元素排序,如果第一个元素相同在根据第二个元素排序!

如上是一个默认二元组排序隐式值,接下来我们就实践一下二元组的排序:

scala> val pairs = nums.map(x => (x,x))//支持两种排序方法sortBy和sortByKey
pairs: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[79] at map at <console>:35
scala> pairs.sortBy(x=>x).collect //使用sortBy算子必须指定排序的Key
res49: Array[(Int, Int)] = Array((15,15), (157,157), (212,212), (224,224), (242,242), (246,246), (293,293), (373,373), (488,488), (513,513))

此处我们指定排序的Key的规则函数是 x=>x,由于OrderedRDDFunctions存在Ordering[Int]所以不需要我们显示指定一个隐式值。



我们也可以对pairs使用sortByKey进行排序:

scala> pairs.sortByKey().collect //此处我们不需要指定key和隐式值
res54: Array[(Int, Int)] = Array((15,15), (157,157), (212,212), (224,224), (242,242), (246,246), (293,293), (373,373), (488,488), (513,513))





3、接下来对自定义类型进行排序,需要我们自己实现Ordering:

当我们不显示提供Dog的Ordering时候,会报错提示没有隐式值:

case class Dog(var age:Int)
val dogs = List(Dog(3), Dog(1), Dog(6), Dog(8))
scala> val dogsRDD = sc.parallelize(dogs)
dogsRDD: org.apache.spark.rdd.RDD[Dog] = ParallelCollectionRDD[97] at parallelize at <console>:35

scala> dogsRDD.map((_,1)).sortBy(x=>x._1)
<console>:31: error: No implicit Ordering defined for Dog.//报错:没有Dog的Ordering隐式值
Error occurred in an application involving default arguments.
       dogsRDD.map((_,1)).sortBy(x=>x._1)




当我们提供一个隐式值:

implicit object DogOrdering extends Ordering[Dog] {
  override def compare(e1:Dog, e2:Dog): Int = {
   (e1.age-e2.age).toInt
  }
}



再次排序:

scala> dogsRDD.map((_,1)).sortBy(x=>x._1).collect
res6: Array[(Dog, Int)] = Array((Dog(1),1), (Dog(3),1), (Dog(6),1), (Dog(8),1))

可以正确排序!


结论:当对Scala系统提供默认的Ordering值时候我们可以不显示给定隐式值,但是如果不存在的话,需要我们在作用域中显示给出!