美文网首页程序员
scala 排序函数使用和代码分析

scala 排序函数使用和代码分析

作者: 小赵营 | 来源:发表于2019-03-21 19:44 被阅读0次

    引用 转载 请注明出处

    序列图

    scala & java 关系不清,爱者,为止疯狂。怨者,无力吐槽。但在一个方面是无可争议的事实、一个让人趋之若鹜特性 -- scala丰富集合库。

    所谓集合库:一种用来存储各种对象和数据的容器,且提供丰富的操作集。本篇我们来介绍scala中集合库常用功能:排序功能。

    浏览API我们会发现,排序的功能存在于多个组合库中,那么

    • 它源于哪里?
    • 有哪些具体操作方法?
    • 如何满足我们的排序需求哪?

    本文将分别说明,并结合示例分析源码。

    操作方法

    在API文档中,List Set Map BitSet Option都存在通用的api,在其中有三个接口,满足我们对排序诉求。函数完整签名如下:

    //sort api 1 2 3
    def sorted[B >: A](implicit ord: Ordering[B]): Repr //1
    def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr //2
    def sortWith(lt: (A, A) => Boolean): Repr //3
    

    api呈现内容:

    • api提供默认排序功能。那么,默认排序是什么哪?

    • api1,2 隐式参数转换是进行排序的基础(关于隐式转换先挖坑,以后写一篇文章),前置提供默认排序方式,后者进行类型转换 A-> B,然后进行排序。2者排序的关键是后面的隐式转换参数 implicit ord: Ordering[B]trait Ordering实现了基本类型的排序方法。

    • api 3 是个似是而非的接口,看似没有Ordering的功能,但实现暴露了本质-- API 1的特殊形式。

      完整实现代码: def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)。sortWith是sorted的封装。

    来源

    排序是scala的集合库提供基础功能。我们在查看基础类继承关系图中,SeqLike总是很特别的一个,我们的排序功能就源于此。为显示方便,UML图中裁剪SeqLike无关函数。

    seqlike uml.png

    蓝色部分scala提供的排序三剑客SeqLike继承和内部实现类,有兴趣可以分析源码。Uml图源于[我的 github开源uml工具]:scaladiagram 分析scala源码生成UML图,阅读源码事半功倍。

    在了解是什么和为什么之后,我们要了解如何用即如何进行排序、以及如何定制化排序?

    排序

    数据排序无外乎升序,降序和自定义排序。我们使用scala的repl进行操作排序,如下:

    • sorted 使用

    scala> List(1,6,4,5).sorted res0: List[Int] = List(1, 4, 5, 6)

    scala> val goat = List("Ronaldo", "Johan Cruyff", "Diego M", "Pele") scala> goat.sorted res0: List[String] = List(Diego M, Johan Cruyff, Pele, Ronaldo)

    以上例子表明scala默认以升序排序。哪如何进行降序排列方法有哪些?

    1. 使用集合类库的reverse函数

    scala> res0.reverse res1: List[String] = List(Ronaldo, Pele, Johan Cruyff, Diego M)

    1. 使用sorted的排序方法

    排序作为通用的方法,scala会提供完善的API的。那么如何使用sorted进行排序哪?猫腻在隐式参数转换Ordering参数上。查询Ordering api中包含reverse可以满足降序排列。

    /** Return the opposite ordering of this one. */
    override def reverse: Ordering[T] = new Ordering[T] {
      override def reverse = outer
      def compare(x: T, y: T) = outer.compare(y, x) //compare方法 x,y有(x,y)变成(y,x)
    }
    

    发现踪迹后,实现降序排列只需要显示调用Ordering参数即可。

    scala> goat.sorted(Ordering[String].reverse) //效果和 goat.sorted.reverse一致 res5: List[String] = List(Ronaldo, Pele, Johan Cruyff, Diego M)

    整型数组按降序进行排序:

    scala> List(1,6,4,5).sorted(Ordering[Int].reverse) res4: List[Int] = List(6, 5, 4, 1) //降序,隐式转换很奇妙呀

    • sortBy排序

    上面的示例是对一种对象类型进行排序,即对整型、字符串的数组进行。在应用时,我们遇到一个对象多属性进行排序,如信息包含个人名称和年龄,从这2个维度进行排序要怎样进行哪?

    val idol = List(("Ronaldo",41), ("Johan Cruyff",72), ("Diego M",56), ("Riva",41))

    这种类型数据, sortBy可解决该问题。

    scala> idol .sortBy{case (name,age) => (age, name)} res6: List[(String, Int)] = List((Riva,41), (Ronaldo,41), (Diego M,56), (Johan Cruyff,72))

    scala> goat.sortBy{case (name,age) => (name, age)} res7: List[(String, Int)] = List((Diego M,56), (Johan Cruyff,72), (Riva,41), (Ronaldo,41))

    按照 age-name排序,注意2种排序方式差异,age相同比较name,Riva\Ronaldo 显示排序的差别。

    name按照降序排序,age按照升序排序,示例:

    scala> goat.sortBy{case (name,age) => (name, age)}(Ordering.Tuple2(Ordering.String.reverse,Ordering.Int)) res9: List[(String, Int)] = List((Ronaldo,41), (Riva,41), (Johan Cruyff,72), (Diego M,56))

    Ordering是伴生对象,sorted中引用的类型是trait。

    sortBy函数签署参数是 f:A=>B表示能够对不同类型的参数进行转换。那么,不同业务排序方式不同,这种情况下,如何进行按照业务进行排序哪?

    • 自定义排序方式

    自定义排序是自己编写排序的方法。

    class Person(id:String,age:Int,address:String) 
    val t:List[Person] = ??? // 对Person的列表进行排序
    //编写person的比较规则,使用隐式转换自动的进行转换
    //编写Person的排序规则也很简单
    object tx {
     trait PersonOrdering extends Ordering[Person] { //比较规则
       def compare(x: Person, y: Person): Int =
        // 排序规则实现, 地址改成小写,然后进行字符串比较
        if(x.address.toLowerCase > y.address.toLowerCase) 1 else -1
     }
     implicit object PersonOrdered extends PersonOrdering //隐式转换
    }
    //如何使用
    import tx._
    t.sorted(Ordering[Person]) 
    

    代码实现的方式是导入object 完成隐式转换,也可以尝试使用 隐式类、隐式函数或变量的方式。

    • sortWith 排序

      sortWith也是自定义排序的一种方式,通过改变参数的类型完成比较。因为原理上使用sorted方法,使用方法和sorted类似,使用方法不赘述。

    总结

    本篇介绍scala里面的三种排序函数,以及如何进行升降序排列。每种都有其各自的应用场景:

    sorted:适合单集合的升降序

    sortBy:适合对单个或多个属性的排序,代码量比较少,推荐使用这种

    sortWith:适合定制化场景比较高的排序规则,用法和sorted类似。

    另外,粗略介绍了隐式转换在排序中使用。

    陌上人如玉,公子世无双. 希望给你带来灵感.

    相关文章

      网友评论

        本文标题:scala 排序函数使用和代码分析

        本文链接:https://www.haomeiwen.com/subject/fixopqtx.html