在Scala中使用什么结构来创建常量访问时不可变表?

时间:2011-08-05 10:51:52

标签: scala collections

Scala有没有可以解决这个问题的类?我正在考虑使用Array[Array[_]],因为它很适合我的需求。

我还有其他选择吗?我相信使用Map[(Int,Int),_]会太慢,因为它具有线性访问时间。

3 个答案:

答案 0 :(得分:4)

HashMap几乎可以做到。这个Scala collection performance页面非常有用。

答案 1 :(得分:4)

地图实现中的访问(除了ListMap)当然不是线性的(非常小的地图,少于4个元素,确实是线性搜索,但对于这么小的地图来说,这是一个非常快速的实现,即O(N)具有非常小的N和非常小的k因子。但是当表增长时,使用不同的算法,渐近地更快,通常是O(log(N))或O(1)。

然而,如果你需要的是一个表/矩阵,在某种意义上它包含满足0< = i<的密钥(i,j)的数据了。 m,0< = j< n,(从1开始也可以)一般地图可能不是最好的选择。如果表格已满,那么所有(i,j)都有值。然后基于数组,Array [Array [_]]或甚至单个数组可能会好得多。如果你想要专门的东西,也许你不想实现完整的Map接口。

答案 2 :(得分:1)

为了获得最佳性能,您可能希望尽可能避免垃圾收集器(GC)。这意味着不创建元组对象(i,j)而不是装箱基元。如果你的表将被完全填充(不稀疏),那么你对阵列数组的想法是合理的。然而,为了获得最佳性能,我会选择didierd的想法并将所有内容打包到一个带有访问器的阵列中。这是一个可能的实现:

// specialize A to avoid boxing primitives such as Int, Double, ...
class Table[@specialized A: Manifest](numRows: Int, numCols: Int) {
  val data = new Array[A](numRows*numCols)
  def checkIndices(i: Int, j: Int) {
    require (i >= 0 && i < numRows && j >= 0 && j < numCols)
  }
  def apply(i: Int, j: Int): A = {
    checkIndices(i, j)
    data(i*numCols + j)
  } 
  def update(i: Int, j: Int, x: A) {
    checkIndices(i, j)
    data(i*numCols + j) = x
  } 
}

您可以像这样自然地使用它(请注意从特殊applyupdate方法获得的漂亮语法):

scala> val table = new Table[Double](2, 2)
table: Table[Double] = Table$mcD$sp@4d22366e

scala> table(0, 0) = 3.0

scala> table(1, 1) = table(0, 0) + 2.0

scala> table(2, 1)
java.lang.IllegalArgumentException: requirement failed
...

另一方面,如果表是稀疏的,那么最好使用Map来为所有空条目保存内存。请注意,尽管Map具有快速updateapply方法(几乎不变),但它们仍然比数组访问慢得多(主要是由于GC上的压力; {{1} }不是专用的,键和值都必须是堆分配的。)