在项目中,我们常常会遇到排序(或比较)需求,比如:对一个Person类

case class Person(name: String, age: Int) {
override def toString = {
"name: " + name + ", age: " + age
}
}

按name值逆词典序、age值升序做排序;在Scala中应如何实现呢?

1. 两个特质

Scala提供两个特质(trait)OrderedOrdering用于比较。其中,Ordered混入(mix)Java的Comparable接口,而Ordering则混入Comparator接口。众所周知,在Java中

  • 实现Comparable接口的类,其对象具有了可比较性;
  • 实现Comparator接口的类,则提供一个外部比较器,用于比较两个对象。

Ordered与Ordering的区别与之相类似:

  • Ordered特质定义了相同类型间的比较方式,但这种内部比较方式是单一的;
  • Ordered则是提供比较器模板,可以自定义多种比较方式。

以下源码分析基于Scala 2.10.5。

Ordered

Ordered特质更像是rich版的Comparable接口,除了compare方法外,更丰富了比较操作(<, >, <=, >=):

trait Ordered[T] extends Comparable[T] {
def compare(that: A): Int
def < (that: A): Boolean = (this compare that) < 0
def > (that: A): Boolean = (this compare that) > 0
def <= (that: A): Boolean = (this compare that) <= 0
def >= (that: A): Boolean = (this compare that) >= 0
def compareTo(that: A): Int = compare(that)
}

此外,Ordered对象提供了从T到Ordered[T]的隐式转换(隐式参数为Ordering[T]):

object Ordered {
/** Lens from `Ordering[T]` to `Ordered[T]` */
implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }
}

Ordering

Ordering,内置函数Ordering.byOrdering.on进行自定义排序:

import scala.util.Sorting
val pairs = Array(("a", 5, 2), ("c", 3, 1), ("b", 1, 3)) // sort by 2nd element
Sorting.quickSort(pairs)(Ordering.by[(String, Int, Int), Int](_._2)) // sort by the 3rd element, then 1st
Sorting.quickSort(pairs)(Ordering[(Int, String)].on(x => (x._3, x._1)))

2. 实战

比较

对于Person类,如何做让其对象具有可比较性呢?我们可使用Ordered对象的函数orderingToOrdered做隐式转换,但还需要组织一个Ordering[Person]的隐式参数:

implicit object PersonOrdering extends Ordering[Person] {
override def compare(p1: Person, p2: Person): Int = {
p1.name == p2.name match {
case false => -p1.name.compareTo(p2.name)
case _ => p1.age - p2.age
}
}
} val p1 = new Person("rain", 13)
val p2 = new Person("rain", 14)
import Ordered._
p1 < p2 // True

Collection Sort

在实际项目中,我们常常需要对集合进行排序。回到开篇的问题——如何对Person类的集合做指定排序呢?下面用List集合作为demo,探讨在scala集合排序。首先,我们来看看List的sort函数:

// scala.collection.SeqLike

def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)

def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr = sorted(ord on f)

def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
...
}

若调用sorted函数做排序,则需要指定Ordering隐式参数:

val p1 = new Person("rain", 24)
val p2 = new Person("rain", 22)
val p3 = new Person("Lily", 15)
val list = List(p1, p2, p3) implicit object PersonOrdering extends Ordering[Person] {
override def compare(p1: Person, p2: Person): Int = {
p1.name == p2.name match {
case false => -p1.name.compareTo(p2.name)
case _ => p1.age - p2.age
}
}
}
list.sorted
// res3: List[Person] = List(name: rain, age: 22, name: rain, age: 24, name: Lily, age: 15)

若使用sortWith,则需要定义返回值为Boolean的比较函数:

list.sortWith { (p1: Person, p2: Person) =>
p1.name == p2.name match {
case false => -p1.name.compareTo(p2.name) < 0
case _ => p1.age - p2.age < 0
}
}
// res4: List[Person] = List(name: rain, age: 22, name: rain, age: 24, name: Lily, age: 15)

若使用sortBy,也需要指定Ordering隐式参数:

implicit object PersonOrdering extends Ordering[Person] {
override def compare(p1: Person, p2: Person): Int = {
p1.name == p2.name match {
case false => -p1.name.compareTo(p2.name)
case _ => p1.age - p2.age
}
}
} list.sortBy[Person](t => t)

RDD sort

RDD的sortBy函数,提供根据指定的key对RDD做全局的排序。sortBy定义如下:

def sortBy[K](
f: (T) => K,
ascending: Boolean = true,
numPartitions: Int = this.partitions.length)
(implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T]

仅需定义key的隐式转换即可:

scala> val rdd = sc.parallelize(Array(new Person("rain", 24),
new Person("rain", 22), new Person("Lily", 15))) scala> implicit object PersonOrdering extends Ordering[Person] {
override def compare(p1: Person, p2: Person): Int = {
p1.name == p2.name match {
case false => -p1.name.compareTo(p2.name)
case _ => p1.age - p2.age
}
}
} scala> rdd.sortBy[Person](t => t).collect()
// res1: Array[Person] = Array(name: rain, age: 22, name: rain, age: 24, name: Lily, age: 15)

3. 参考资料

[1] Alvin Alexander, How to sort a sequence (Seq, List, Array, Vector) in Scala.

Scala比较器:Ordered与Ordering的更多相关文章

  1. Scala比较器---Ordered与Ordering

    1.Ordered 和 Ordering Scala提供两个特质(trait)Ordered与Ordering用于比较.其中,Ordered混入(mix)Java的Comparable接口,而Orde ...

  2. Scala进阶之路-Scala中的Ordered--Ordering

    Scala进阶之路-Scala中的Ordered--Ordering 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   说道对象的比较,在Java中大家最熟悉不过的就是实现类本身实 ...

  3. Scala 学习之路(十二)—— 类型参数

    一.泛型 Scala支持类型参数化,使得我们能够编写泛型程序. 1.1 泛型类 Java中使用<>符号来包含定义的类型参数,Scala则使用[]. class Pair[T, S](val ...

  4. Scala 系列(十二)—— 类型参数

    一.泛型 Scala 支持类型参数化,使得我们能够编写泛型程序. 1.1 泛型类 Java 中使用 <> 符号来包含定义的类型参数,Scala 则使用 []. class Pair[T, ...

  5. Scala HandBook

    目录[-] 1.   Scala有多cool 1.1.     速度! 1.2.     易用的数据结构 1.3.     OOP+FP 1.4.     动态+静态 1.5.     DSL 1.6 ...

  6. Scala 深入浅出实战经典 第62讲:Scala中上下文界定内幕中的隐式参数实战详解

    王家林亲授<DT大数据梦工厂>大数据实战视频 Scala 深入浅出实战经典(1-87讲)完整视频.PPT.代码下载: 百度云盘:http://pan.baidu.com/s/1c0noOt ...

  7. Scala编程基础

    Scala与Java的关系... 4 安装Scala. 4 Scala解释器的使用... 4 声明变量... 5 数据类型与操作符... 5 函数调用与apply()函数... 5 if表达式... ...

  8. scala 基础语法

    文章内容全部来自:http://twitter.github.io/scala_school/zh_cn/index.html 表达式 scala> 1 + 1 res0: Int = 2 值 ...

  9. 大数据学习day17------第三阶段-----scala05------1.Akka RPC通信案例改造和部署在多台机器上 2. 柯里化方法 3. 隐式转换 4 scala的泛型

    1.Akka RPC通信案例改造和部署在多台机器上  1.1 Akka RPC通信案例的改造(主要是把一些参数不写是) Master package com._51doit.akka.rpc impo ...

随机推荐

  1. String.Format 格式说明

    C#格式化数值结果表 字符 说明 示例 输出 C 货币 string.Format("{0:C3}", 2) $2.000 D 十进制 string.Format("{0 ...

  2. SharePoint 2010 Survey的Export to Spreadsheet功能怎么不见了?

    背景信息: 最近用户报了一个问题,说他创建的Survey里将结果导出成Excel文件(Export to spreadsheet)的按钮不见了. 原因排查: 正常情况下,这个功能只存在于SharePo ...

  3. ABP理论学习之仓储

    返回总目录 本篇目录 IRepository接口 查询 插入 更新 删除 其他 关于异步方法 仓储实现 管理数据库连接 仓储的生命周期 仓储最佳实践 Martin Fowler对仓储的定义 位于领域层 ...

  4. Python黑帽编程 3.2 ARP监控

    Python黑帽编程 3.2 ARP监控 在第3.1节<ARP欺骗>中,我们学习了ARP的基本原理,使用Python实现了我们自己的ARP欺骗工具.在上一节的基础上,我们来实现一个ARP监 ...

  5. 关于大型网站技术演进的思考(二十)--网站静态化处理—web前端优化—中(12)

    Web前端很多优化原则都是从如何提升网络通讯效率的角度提出的,但是这些原则使用的时候还是有很多陷阱在里面,如果我们不能深入理解这些优化原则背后所隐藏的技术原理,很有可能掉进这些陷阱里,最终没有达到最佳 ...

  6. 关于大型网站技术演进的思考(十九)--网站静态化处理—web前端优化—上(11)

    网站静态化处理这个系列马上就要结束了,今天我要讲讲本系列最后一个重要的主题web前端优化.在开始谈论本主题之前,我想问大家一个问题,网站静态化处理技术到底是应该归属于web服务端的技术范畴还是应该归属 ...

  7. MapReduce剖析笔记之一:从WordCount理解MapReduce的几个阶段

    WordCount是一个入门的MapReduce程序(从src\examples\org\apache\hadoop\examples粘贴过来的): package org.apache.hadoop ...

  8. Python 学习文章收藏

    作者 标题 rollenholt Python修饰器的函数式编程 - Rollen Holt - 博客园 rollenholt python操作gmail - Rollen Holt - 博客园 ro ...

  9. ASP.NET MVC 5 - 验证编辑方法(Edit method)和编辑视图(Edit view)

    在本节中,您将验证电影控制器生成的编辑方法(Edit action methods)和视图.但是首先将修改点代码,使得发布日期属性(ReleaseDate)看上去更好.打开Models \ Movie ...

  10. JS实战 · 表格行颜色间隔显示,并在鼠标指定行上高亮显示

    思路: 1.获取所有行对象,将需要间隔颜色显示的行对象进行动态的className属性指定:      前提是:先定义好类选择器,就是说给行对象赋予name. 2.高亮用到两个事件:onmouseov ...