Scala:最有效地按第三个值排序(Int,Int,Int)的ListBuffer

时间:2016-11-20 23:53:16

标签: scala sorting listbuffer

我希望最有效地按第三个值排序ListBuffer(Int, Int, Int)。这就是我现在拥有的。我正在使用sortBy。注意yz都是ListBuffer[(Int, Int, Int)],我首先要解决这个问题。我的目标是优化它并最有效地执行此操作(取两个列表之间的差异并按第三个元素排序)。我假设diff部分无法优化,但sortBy可以,所以我正在寻找一种有效的方法来做排序部分。我找到了关于排序数组的帖子,但我正在使用ListBuffer并转换为数组会增加开销,所以我宁愿不转换我的ListBuffer。

val x = (y diff z).sortBy(i => i._3)

1 个答案:

答案 0 :(得分:5)

1)如果您想使用Scala库,那么您无法做得更好。 Scala已经尝试以最有效的方式对您的集合进行排序。 SeqLike定义调用此实现的def sortBy[B](f: A => B)(implicit ord: Ordering[B]): Repr = sorted(ord on f)

  def sorted[B >: A](implicit ord: Ordering[B]): Repr = {
    val len = this.length
    val arr = new ArraySeq[A](len)
    var i = 0
    for (x <- this.seq) {
      arr(i) = x
      i += 1
    }
    java.util.Arrays.sort(arr.array, ord.asInstanceOf[Ordering[Object]])
    val b = newBuilder
    b.sizeHint(len)
    for (x <- arr) b += x
    b.result
  }

这是您的代码将调用的内容。如您所见,它已经使用数组来对数据进行排序。根据{{​​1}}的{​​{3}}:

  

实施说明:此实施是稳定的,适应性的,   迭代合并,需要远远少于n lg(n)的比较   当输入数组部分排序时,同时提供   输入数组时传统mergesort的性能   随机排序。如果输入数组几乎排序,那么   实施需要大约n次比较。

2)如果您尝试通过将diff的结果直接插入排序结构(如二叉树)进行优化,因为您逐个元素生成它们,您仍将支付相同的价格:平均插入费用为public static void sort(Object[] a)log(n)个元素n - 与合并排序等快速排序算法相同。

3)因此,除非您针对特定用例进行优化,否则无法对此案例进行优化。

3a)例如,= n log(n)可能会替换为ListBufferSet应该更快。事实上,它实现为:

diff

使用的 def diff(that: GenSet[A]): This = this -- that 反过来应该比-上的diff更快,而Seq必须首先构建地图:

def diff[B >: A](that: GenSeq[B]): Repr = {
    val occ = occCounts(that.seq)
    val b = newBuilder
    for (x <- this)
      if (occ(x) == 0) b += x
      else occ(x) -= 1
    b.result
  }

3b)您还可以使用_3作为数组中的索引来避免排序。如果使用该索引插入,则将对数组进行排序。这仅在您的数据足够密集或者您很乐意随后处理稀疏数组时才有效。一个索引也可能有多个值映射到它,你也必须处理它。实际上,您正在构建有序地图。您也可以使用Map,但HashMap不会被排序,TreeMap将需要log(n)再次添加操作。

咨询javadoc,了解根据您的情况可以获得的收益。

4)无论如何,在现代计算机上排序真的很快。做一些基准测试,以确保您不会过早地优化它。

总结不同情景的复杂性......

您目前的情况:

  • 差异SeqLikenthat + n创建地图以迭代this(地图查找实际上是恒定时间(C) )= 2nO(n)
  • 排序 - O(n log(n))
  • 总计= O(n) + O(n log(n)) = O(n log(n)),更确切地说:2n + nlog(n)

如果您使用Set代替SeqLike

  • 差异Setn要迭代(查询为C)= O(n)
  • sort - same
  • 总计 - 相同:O(n) + O(n log(n)) = O(n log(n)),更确切地说:n + nlog(n)

如果使用Set和数组插入:

  • diff - 与Set
  • 相同
  • sort - 0 - 数组按构造排序
  • 总计:O(n) + O(0) = O(n),更确切地说:n。对于稀疏数据可能不太实用。

在宏观方案中看起来并不重要,除非你有一个独特的案例,从最后一个选项(数组)中受益。

如果你有ListBuffer[Int]而不是ListBuffer[(Int, Int, Int)]我会建议先对两个集合进行排序,然后通过同时对它们进行单次传递来做差异。这将是O(nlog(n))。在您的情况下,_3排序不足以保证两个集合中的确切顺序。您可以按元组的所有三个字段进行排序,但这会改变原始顺序。如果您对此感到满意并编写自己的差异,那么它可能是最快的选择。