程序员

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

2019-03-21  本文已影响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呈现内容:

来源

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

seqlike uml.png

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

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

排序

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

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) //降序,隐式转换很奇妙呀

上面的示例是对一种对象类型进行排序,即对整型、字符串的数组进行。在应用时,我们遇到一个对象多属性进行排序,如信息包含个人名称和年龄,从这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 完成隐式转换,也可以尝试使用 隐式类、隐式函数或变量的方式。

总结

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

sorted:适合单集合的升降序

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

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

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

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

上一篇下一篇

猜你喜欢

热点阅读