Для максимальной производительности вы, вероятно, захотите максимально избегать сборщика мусора (GC). Это означает, что нельзя создавать объекты кортежей (i, j) и не создавать примитивы бокса. Если ваша таблица будет полностью заполнена (а не разрежена), то ваша идея Array of Arrays разумна. Однако для лучшей производительности я бы пошел с идеей Дидьера и собрал все в единый массив с аксессорами. Вот возможная реализация:
// 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
(почти постоянные), они все же немного медленнее, чем доступ к массиву (в основном из-за давления на ГХ; Map
не специализирован, оба ключа и значения должны быть выделены в куче).