Как использовать ListBuffer в Scala? - PullRequest
8 голосов
/ 04 июля 2011

У меня есть ситуация, когда я передаю один элемент списка в метод. В этом методе единственный элемент в списке увеличивается на единицу. Так что после вызова метода первый элемент списка изменяется (увеличивается на единицу).

Код такой:

val ct = List(5)

someMethod(ct)

println (ct(0))

// should print 6

......

//within somethod, I incrment the element like:
def someMethod(ct: List[Int}) {
    ct(0) = ct(0) + 1
}

Конечно, приведенный выше код не работает в Scala. Я посмотрел на ListBuffer , но мне было трудно следить за документом scala. Scala doc разделен на 2 группы: члены типа и члены значения. В типе member есть класс WithFiler, а значение member имеет много методов. Как я могу использовать WithFiler (вероятно, не имеет прямого отношения к этому вопросу, но я хочу понять, как использовать Scala Doc).

ListBuffer, кажется, является правильным решением этой проблемы, если я хочу иметь очень высокую производительность (метод someMethod вызывается миллионы раз) (поправьте меня, если я ошибаюсь).

Так, как решить вышеупомянутую проблему, если ListBuffer - правильный тип списка, и если нет, каково решение?

Ответы [ 5 ]

10 голосов
/ 04 июля 2011

В скале выражение:

ct(0) = ct(0) + 1

переписывается как:

ct.update( 0, ct.apply(0) + 1 )

Метод update не определен для супертипа List, поскольку списки могут быть неизменными. Однако это тип аргумента функции.

Таким образом, вы должны использовать только ListBuffers (или изменяемый супертип):

def someMethod(ct: ListBuffer[Int]) {
  ct(0) = ct(0) + 1
}

scala> val lst = ListBuffer( 5 )
  lst: scala.collection.mutable.ListBuffer[Int] = ListBuffer(5)
scala> someMethod( lst )
scala> lst
  res2: scala.collection.mutable.ListBuffer[Int] = ListBuffer(6)

Кстати, если вам нужен доступ к элементам по индексам, используйте вместо этого ArrayBuffer. Это должно работать как Java ArrayList.

Наконец, если вам не нужно думать о WithFilter вещах. Просто используйте метод filter.

5 голосов
/ 04 июля 2011

Это запах преждевременной микрооптимизации.

Хотя существуют веские причины для использования изменчивости (включая оптимизацию), вы не указали, почему вы считаете, что ваше использование является допустимым, или большую проблему, которую вы пытаетесь решить. В частности, неизменные списки эффективны очень при взятии хвоста и добавлении нового заголовка - 100% элементов, не являющихся заголовками, будут разделены между исходным и новым списком.

Как показано, самое чистое решение для ваших требований - забыть о ListBuffer, придерживаться неизменного List и реализовать someMethod без использования побочных эффектов.

def someMethod(xs: List[Int]) = xs match {
  case h :: t => (h+1) :: t
  case Nil => Nil
}

val ct = List(5)
println (someMethod(ct).headOption getOrElse "empty list")

// should print 6

С другой стороны, если является точкой доступа к производительности, и вы не можете определить какой-либо способ повышения производительности путем изменения алгоритма, тогда вам нужно будет использовать Array

Массив - это единственный тип сбора данных в JVM. Таким образом, вы можете напрямую работать с примитивами в массиве и избежать упаковки / распаковки, с которой приходится сталкиваться другим типам коллекций.

Преимущество здесь не имеет ничего общего с изменчивостью и неизменностью; стоимость производительности распаковки / упаковки намного выше, чем стоимость производительности (если таковая имеется) при использовании неизменяемого Списка.

5 голосов
/ 04 июля 2011

Если производительность является основной целью и известен максимальный размер коллекции, вы можете использовать массив, который напрямую сопоставляется с массивом Java.

2 голосов
/ 04 июля 2011

Если вы хотите использовать ListBuffer, все, что вам нужно сделать, это заменить слово «List» в вашем коде на «ListBuffer». Тем не менее, я бы избегал использования функции побочных эффектов (ваш someMethod: Unit) полностью, как предполагает Натен.

ListBuffer обладает «высокой производительностью» - однако основная цель - добавить элементы / изменить коллекцию. Если я правильно понимаю, вы просто обновляете элемент в одной коллекции элементов миллион раз - для этого достаточно массива.

2 голосов
/ 04 июля 2011

Вы, конечно, можете переписать вышеприведенное, чтобы использовать ListBuffer, но если все, что вам нужно, это семантика вызова по ссылке для одного Int, то это не обязательно самое эффективное решение.ArrayBuffer должно быть немного лучше.Вы также можете сделать что-то вроде этого:

class IntCell(var x: Int)
def someMethod(ct: IntCell) {
  ct.x += 1
}

Я обычно пытаюсь переписать свой код, чтобы избежать использования изменяемых коллекций для такого рода вещей.Например, вы можете просто вернуть новое значение:

def someMethod(ct: Int) = ct + 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...