如何在Scala中对数组进行排序?

89

我看到这里有一个排序对象Sorting,带有一个名为quickSort快速排序方法。

请问如何使用它来对任意类型的对象数组进行排序?看起来我需要传入一个实现了Orderable特性的对象,但我不确定语法应该怎么写。

此外,我更倾向于按照Scala方式完成。虽然我知道可以使用Java库来完成。

7个回答

109

使用Scala 2.8或更高版本,可以进行以下操作:

List(3,7,5,2).sortWith(_ < _)

使用 java.util.Arrays.sort 的快速排序算法实现。


3
使用隐式排序,更简短的写法是:List(3,7,5,2).sorted 根据scala.collection.immutable.LinearSeq - Utgarda
我们如何知道sortWith使用的是java.util.Arrays.sort?有参考资料吗? - deepkimo
在源代码 https://github.com/scala/scala/blob/2.12.x/src/library/scala/collection/SeqLike.scala#L618 中的 @deepkimo。 - adelarsq
这与原问题有些不同。它确实可以创建一个新的已排序列表(或数组,如原问题所述),但sortWith返回一个新对象,而不是在原地对列表或数组进行排序。 - rphutchinson

63

现在这个也可以:

List(3,7,5,2).sorted


1
我想要进行反向排序,惯用的方式是 List(3,7,5,2).sorted.reverse 吗? - Nick Chammas

32

Sorting.quickSort声明了用于对数字或字符串数组进行排序的函数,但我猜你想要对自己定义的类对象列表进行排序?

我认为你在寻找的函数是

quickSort [K](a : Array[K])(implicit view$1 : (K) => Ordered[K]) : Unit

如果我理解正确的话,这意味着Array中的对象必须具有Ordered特质。因此您的类必须扩展Ordered(或混入它),因此必须实现该特质的compare方法。

那么我们从书中拿一个例子来看:

class MyClass(n: Int) extends Ordered[MyClass] {
   ...
  def compare(that: MyClass) =
    this.n - that.n
}

因此,如果给定一个Array[MyClass],那么Sorting.quickSort应该可以工作。


是的,就是那个。你指出来后现在很有道理。我该如何“混合它进去”? - Dan Gravell
请参考我的示例进行扩展,或者使用“class MyClass extends OtherClass with Ordered[MyClass]”这种混合方式。这是一种混入方法。 - skaffman
谢谢。我想我会选择后者。 - Dan Gravell
6
不想显得学究,但是...你的类不需要扩展Ordered,只需在作用域内存在一个从你的类到扩展了Ordered的某个东西的视图(也称为隐式转换)即可。 - Kim Stebel

19

如果您只想对事物进行排序,但不必一定使用 Sorting 对象,可以使用 List 的 sort 方法。它接受一个比较函数作为参数,因此可以将其用于任何类型:

List("Steve", "Tom", "John", "Bob").sort((e1, e2) => (e1 compareTo e2) < 0)

List(1, 4, 3, 2).sort((e1, e2) => (e1 < e2))

列表可能比数组更符合Scala语言的特点。

引自Scala API 文档

def sort(lt: (A, A) => Boolean): List[A]

Sort the list according to the comparison function <(e1: a, e2: a) =>

布尔值,当且仅当e1小于e2时应为true。


嗯,List有一个sort方法,但Array没有。多么不令人满意的不规则性。我期望这种无聊的事情发生在Java API中,而不是Scala。 - skaffman
我是一个Scala新手,不太确定,但考虑到Scala需要与所有Java相关的东西保持兼容性,这可能是必要的。另一方面,Scala做了很多魔法,想让它做更多的事情似乎也很正常! - Peter Recore
5
使用 List(...) sort { _ < _ } 更符合惯用语。 - Daniel Spiewak
2
值得一提的是,通过隐式转换,String确实定义了<方法:"daniel"<"chris"==false。 - Daniel Spiewak
11
仅供参考,对于Scala 2.9.1而言,sort方法已被弃用,请使用sortWith方法。 - Jérôme

5
val array = Array((for(i <- 0 to 10) yield scala.util.Random.nextInt): _*)
scala.util.Sorting.quickSort(array)

Scala的“默认”数组是一种可变数据结构,非常接近于Java的数组。一般来说,这意味着“数组”即使作为可变数据结构也不是很符合Scala的风格。不过,它确实有其用途。如果数组是您需要的正确数据类型,那么就使用它进行排序。顺便说一下,对象Sorting上还有其他排序方法。
我想我刚刚意识到了你的问题……你不需要传递任何隐式参数(毕竟它是隐式的)。该参数存在的目的是表明必须有某种方式将类型K转换为Ordered[K]。对于Scala的类,这些定义已经存在,因此您不需要它们。
对于任意类,您可以按以下方式定义它:
scala> case class Person(name: String)
defined class Person

scala> val array = Array(Person("John"), Person("Mike"), Person("Abe"))
array: Array[Person] = Array(Person(John), Person(Mike), Person(Abe))

scala> scala.util.Sorting.quickSort(array)
<console>:11: error: no implicit argument matching parameter type (Person) => Ordered[Person] was found.
       scala.util.Sorting.quickSort(array)
                                   ^
scala> class OrderedPerson(val person: Person) extends Ordered[Person] {
     | def compare(that: Person) = person.name.compare(that.name)
     | }
defined class OrderedPerson

scala> implicit def personToOrdered(p: Person) = new OrderedPerson(p)
personToOrdered: (p: Person)OrderedPerson

scala> scala.util.Sorting.quickSort(array)

scala> array
res8: Array[Person] = Array(Person(Abe), Person(John), Person(Mike))

现在,如果 Person 一开始就被命令执行,这个问题就不会存在:

scala> case class Person(name: String) extends Ordered[Person] {
     | def compare(that: Person) = name.compare(that.name)
     | }
defined class Person

scala> val array = Array(Person("John"), Person("Mike"), Person("Abe"))
array: Array[Person] = Array(Person(John), Person(Mike), Person(Abe))

scala>  scala.util.Sorting.quickSort(array)

scala> array
res10: Array[Person] = Array(Person(Abe), Person(John), Person(Mike))

抱歉,我的表述可能不够清楚:我需要对一个任意类型的对象数组进行排序,而不是数字。 - Dan Gravell

4
我更喜欢使用排序工具 示例:
val arr = Array(7,5,1, 9,2)

scala.util.Sorting.quickSort(arr)

请阅读以下链接了解更多信息:排序工具

为什么要链接到旧的2.9.0 API文档,而不是当前文档 - jwvh

3

虽然被接受的答案并没有错,但快速排序方法提供了比那更多的灵活性。我为您编写了这个示例。

import System.out.println
import scala.util.Sorting.quickSort

class Foo(x:Int) {
def get = x
}

//a wrapper around Foo that implements Ordered[Foo]
class OrdFoo(x:Foo) extends Ordered[Foo] {
def compare(that:Foo) = x.get-that.get
}
//another wrapper around Foo that implements Ordered[Foo] in a different way
class OrdFoo2(x:Foo) extends Ordered[Foo] {
def compare(that:Foo) = that.get-x.get
}
//an implicit conversion from Foo to OrdFoo
implicit def convert(a:Foo) = new OrdFoo(a)

//an array of Foos
val arr = Array(new Foo(2),new Foo(3),new Foo(1))

//sorting using OrdFoo
scala.util.Sorting.quickSort(arr)
arr foreach (a=>println(a.get))
/*
This will print:
1
2
3
*/

//sorting using OrdFoo2
scala.util.Sorting.quickSort(arr)(new OrdFoo2(_))
arr foreach (a=>println(a.get))
/*
This will print:
3
2
1
*/

这展示了如何使用从Foo到继承自Ordered[Foo]类的某个类的隐式和显式转换来获得不同的排序顺序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接