Scala 常用Sort排序

Scala 使用过程中经常要要用到排序模块,自带的方法有sorted,sortBy,sortWith,同时也提供了接口和类,例如Ordering,Ordered,Comparable,Comparator等,下面就盘一下他们的基本用法:

 

一.sorted

1.sorted-Default

sorted 是基于Ordering 排序

  /** Sorts this $coll according to an Ordering.
   *
   *  The sort is stable. That is, elements that are equal (as determined by
   *  `lt`) appear in the same order in the sorted sequence as in the original.
   *
   *  @see [[scala.math.Ordering]]
   *
   *  @param  ord the ordering to be used to compare elements.
   *  @return     a $coll consisting of the elements of this $coll
   *              sorted according to the ordering `ord`.
   */
  def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
    val len = this.length
    val b = newBuilder
    if (len == 1) b ++= this
    else if (len > 1) {
      b.sizeHint(len)
      val arr = new Array[AnyRef](len)  // Previously used ArraySeq for more compact but slower code
      var i = 0
      for (x <- this) {
        arr(i) = x.asInstanceOf[AnyRef]
        i += 1
      }
      java.util.Arrays.sort(arr, ord.asInstanceOf[Ordering[Object]])
      i = 0
      while (i < arr.length) {
        b += arr(i).asInstanceOf[A]
        i += 1
      }
    }
    b.result()
  }

通过源码我们可以看到sorted有一个隐式参数,这个隐式参数后续可以自己定义排序方法从而让sorted在无形中改变排序规则,另一点,sorted是基于scala编写,但底层调用的是java.util.Arrays.sort。sorted是默认的排序,在scala里适合对单集合进行排序,例如Array[T],ArrayBuffer[T]。sorted默认从小到大排序,如果想要逆序就再调用.reverse即可

test:

    val arr = Array(0,2,1)
    arr.sorted.reverse.foreach(println)

最常见的数组排序,然后逆序返回: 

2
1
0

 

2.sorted-Ordering

trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable

Ordering是继承Compartor实现的接口,其中实现了基本的比大小,最大最小还有反转等基本功能,这里内部的reverse函数比较好玩,reverse实现就是把compare(x:T, y:T)的参数互换位置,实现反比,有兴趣可以看看~ 另一个Ordered和Ordering实现的功能类似,但是使用起来Ordering灵活

test:

// 用户类
class Person(name:String,age:Int) {
  val name = name
  val age = age
}

object CompareWithTrait {
  def main(args: Array[String]): Unit = {
    val p1 = new Person("a", 24)
    val p2 = new Person("a", 22)
    val p3 = new Person("b", 15)
    val list = List(p1, p2, p3)

    // sorted接口需要
    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.foreach(x => println(x.name,x.age))
}

通过定义源码中的隐函数参数,实现了自定义sorted。这里首先定义一个Demo的用户类,只有两个简单属性name和age,下面测试函数我们只需要初始化几个用户,然后自己重写override compare方法,注意这里采用implicit隐式定义,正常情况下如果我们使用别人提供的包,一些隐式方法我们是感知不到的,例如我会默认把我类里的Int变成String,这样我有其他函数需要传String参数时,传Int也不会报错;

  implicit def intToString(number: Int): String ={
    number.toString
  }

这里隐式函数就是给Sorted使用,正常情况我们也无法感知,compare通过返回的Int判断两边的大小,大于0是前者大于后者,小于0同理,等于0就默认顺序了。这里compare首先按照姓名逆序排列,然后再按照年龄顺序排列,所以看下结果:

(b,15)
(a,22)
(a,24)

Ordered的写法这里就不写了,用了一下确实没有Ordering好使~

 

3.sorted-Comparable

刚才的Ordering继承了Comparable,二者一个是scala的接口,一个是java的接口,这里略去很多的注释,这个接口我们只需要实现compareTo,与Ordering类似,需要通过Int的正负返回比较的两个对象的大小,o就是要比的,this就是自己

public interface Comparable {
    /**
     ...
     * @param   o the object to be compared.
     * @return  a negative integer, zero, or a positive integer as this object
     *          is less than, equal to, or greater than the specified object.
     *
     * @throws NullPointerException if the specified object is null
     * @throws ClassCastException if the specified object's type prevents it
     *         from being compared to this object.
     */
    public int compareTo(T o);
}

test:

object CompareWithComparable {
  def main(args: Array[String]): Unit = {
    val p1 = new CompareWithComparable(5)
    val p2 = new CompareWithComparable(10)
    val p3 = new CompareWithComparable(8)
    val stArr = Array(p1,p2,p3)
    stArr.sorted.foreach(x => println(x.age))
  }
}

class CompareWithComparable(val age: Int) extends java.lang.Comparable[CompareWithComparable] {

  override def compareTo(p: CompareWithComparable): Int = {
    this.age - p.age
  }
}

和上面的person类似,这里只是简单的比较了年龄大小,当然里面也可以写很复杂的逻辑,不过这里我们应该遵循如下规则,否则代码将会出现一些奇怪的异常:

对于所有的x,y,z

tips:sgn是常用的符号函数,返回+1,-1,0 

对称性: sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) 

传递性: x.compareTo(y) > 0 && y.compareTo(z) > 0 则 x.compareTo(z) > 0

等价替换: x.compareTo(y) == 0 则 sgn(x.compareTo(z)) == sgn(y.compareTo(z))

相等性(待定): x.compareTo(y) == 0 等价于 x.equals(y)

前3个特性和重写equals的要求比较类似,第四个则并不是需要严格执行,参考sorted-Ording例子中,我们有可能存在两个人同名,同龄,但是其他属性不同,所以这里compare并不像equals那样严格要求相等。还有一个类似的comparator接口

public interface Comparator {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

两个compare的区别是前者需要override compare()方法,后者则override compareTo方法,上面说到的Ordering其实就是继承了Comparator,所以我们复写了compare方法。

如果不定义隐函数或者继承Comparable直接对一个类进行sorted会报错:

Error:(35, 22) No implicit Ordering defined for xxx

 

 

二.SortedBy

sortedBy需要指定你要通过哪个元素排序,可以针对普通数组,也可以针对有参数的类

1.普通数组

    val arr = Array((1,2),(2,1),(3,3))
    arr.sortBy(_._2).foreach(println)

 指定通过元祖的第二位排序

(2,1)
(1,2)
(3,3)

 

2.类

class person(val age: Int) {
  def getAge(): Int = age
} 

val arr = Array(new person(1),new person(10),new person(5))
arr.sortBy(-_.getAge()).foreach(p => println(p.getAge()))

简单类的排序,通过-号可以实现逆序

10
5
1

 

三.Sort with

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

前面的排序通过返回的Int做排序的判断依据,sortWith采用Boolean,换汤不换药,把减法变成了判断两个变量大小,这里看源码发现其实sortWith也是sorted的一种特殊形态

test:

    def fn(p1: person, p2: person) = (p1.age > p2.age)
    A:arr.sortWith(fn).foreach(p => println(p.age))
    B:arr.sortWith((p1,p2) => p1.age > p2.age).foreach(p => println(p.age))

通过定义返回boolean的函数,判断两个类,A,B两种写法均可,A适合写复杂逻辑

10
5
1

 

你可能感兴趣的:(Scala,常用语法,scala,sort)