为了获得最佳性能,您可能希望尽可能避免使用垃圾收集器 (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
}
}
您可以像这样自然地使用它(注意您从特殊的 apply 和 update 方法中获得的漂亮语法):
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 具有快速的update 和apply 方法(几乎恒定),但它们仍然比数组访问慢很多(主要是由于GC 的压力;Map 不是专门的,这两个键并且值必须是堆分配的)。