Есть ли в Scala какие-либо рекомендации относительно того, когда использовать val с изменяемой коллекцией по сравнению с использованием var с неизменяемой коллекцией? Или вам действительно стоит стремиться к val с неизменной коллекцией?
Тот факт, что существуют оба типа коллекции, дает мне большой выбор, и часто я не знаю, как сделать этот выбор.
scala
collections
functional-programming
immutability
Джеймс МакКейб
источник
источник
Ответы:
Довольно распространенный вопрос. Труднее всего найти дубликаты.
Вы должны стремиться к ссылочной прозрачности . Это означает, что, если у меня есть выражение «е», я могу составить a
val x = e
и заменитьe
наx
. Это свойство, которое нарушает изменчивость. Всякий раз, когда вам нужно принять дизайнерское решение, максимизируйте ссылочную прозрачность.С практической точки зрения, локальный метод
var
является наиболее безопасным изvar
существующих, поскольку он не ускользает от метода. Если способ короткий, то еще лучше. Если это не так, попробуйте уменьшить его, извлекая другие методы.С другой стороны, изменчивая коллекция может ускользнуть, даже если этого не произойдет. При изменении кода вы можете затем передать его другим методам или вернуть. Такие вещи нарушают ссылочную прозрачность.
С объектом (полем) происходит примерно то же самое, но с более ужасными последствиями. В любом случае объект будет иметь состояние и, следовательно, нарушить ссылочную прозрачность. Но наличие изменяемой коллекции означает, что даже сам объект может потерять контроль над тем, кто его изменяет.
источник
immutable val
болееimmutable var
надmutable val
болееmutable var
. Особенноimmutable var
конченоmutable val
!var
. Еще одна приятная особенность использования неизменяемых коллекций заключается в том, что вы можете эффективно хранить старые копии, даже если ониvar
видоизменяются.var x: Set[Int]
болееval x: mutable.Set[Int]
, так как если вы передадитеx
в какую - то другую функцию, в первом случае, вы уверены, что функция не может мутироватьx
для вас.Если вы работаете с неизменяемыми коллекциями и вам нужно их «модифицировать», например, добавлять в них элементы в цикле, тогда вам нужно использовать
var
s, потому что вам нужно где-то хранить полученную коллекцию. Если вы читаете только из неизменяемых коллекций, используйтеval
s.В общем, убедитесь, что вы не путаете ссылки и объекты.
val
s - неизменяемые ссылки (постоянные указатели в C). То есть при использованииval x = new MutableFoo()
вы сможете изменить объект, на которыйx
указывает, но не сможете изменить, на какой объектx
указывает. Обратное верно, если вы используетеvar x = new ImmutableFoo()
. Продолжая свой первоначальный совет: если вам не нужно менять, на какой объект ориентируются, используйтеval
s.источник
var immutable = something(); immutable = immutable.update(x)
побеждает цель использования неизменяемой коллекции. Вы уже отказались от ссылочной прозрачности и обычно можете получить тот же эффект от изменяемой коллекции с лучшей временной сложностью. Из четырех возможностей (val
иvar
, изменяемые и неизменяемые), это один делает наименьшее смысл. Я часто пользуюсьval mutable
.var list: List[X] = Nil; list = item :: list; ...
и я забыл, что когда-то писал по-другому.Лучший способ ответить на этот вопрос - привести пример. Предположим, у нас есть процесс, по какой-то причине просто собирающий числа. Мы хотим записать эти числа и отправить коллекцию в другой процесс для этого.
Конечно, мы продолжаем собирать числа после того, как отправим их в регистратор. И предположим, что в процессе регистрации есть некоторые накладные расходы, которые задерживают фактическое ведение журнала. Надеюсь, вы видите, к чему это идет.
Если мы сохраняем эту коллекцию в изменяемом
val
(изменяемом, потому что мы постоянно добавляем к нему), это означает, что процесс, выполняющий ведение журнала, будет смотреть на тот же объект, который все еще обновляется нашим процессом сбора. Эта коллекция может быть обновлена в любое время, поэтому, когда пришло время регистрировать, мы можем фактически не регистрировать отправленную коллекцию.Если мы используем неизменяемый
var
, мы отправляем неизменяемую структуру данных в регистратор. Когда мы добавляем больше чисел в нашу коллекцию, мы будем заменять OURvar
с новыми непреложными структурами данных . Это не означает, что коллекция, отправленная в регистратор, заменена! Он по-прежнему ссылается на отправленную коллекцию. Таким образом, наш регистратор действительно регистрирует полученную коллекцию.источник
Я думаю, что примеры в этом сообщении в блоге прольют больше света, поскольку вопрос о том, какую комбинацию использовать, становится еще более важным в сценариях параллелизма: важность неизменности для параллелизма . И пока мы занимаемся этим, обратите внимание на предпочтительное использование synchronized и @volatile по сравнению с чем-то вроде AtomicReference: три инструмента
источник
var immutable
vs.val mutable
Помимо множества отличных ответов на этот вопрос. Вот простой пример, иллюстрирующий потенциальную опасность
val mutable
:Изменяемые объекты можно изменять внутри методов, которые принимают их как параметры, при этом переназначение не допускается.
Результат:
Ничего подобного не может произойти с
var immutable
, потому что переназначение недопустимо:Результаты в:
Поскольку параметры функции обрабатываются,
val
это переназначение недопустимо.источник
mutable val
невозможно сimmutable var
. Что здесь неверного?+=
метода, как буфер массива. Ваши ответы подразумевают, что+=
это то же самое,x = x + y
что и не так. Ваше утверждение о том, что параметры функции обрабатываются как vals, является правильным, и вы получаете указанную ошибку, но только потому, что вы использовали=
. Вы можете получить ту же ошибку с ArrayBuffer, поэтому изменчивость коллекций здесь не имеет значения. Так что это не лучший ответ, потому что он не понимает того, о чем говорит OP. Хотя это хороший пример опасности передачи изменяемой коллекции, если вы этого не собирались.ArrayBuffer
, используяVector
. Вопрос OP обширен, но они искали предложения о том, когда использовать что, поэтому я считаю, что мой ответ полезен, потому что он иллюстрирует опасности передачи изменяемой коллекции (факт, чтоval
это не помогает);immutable var
безопаснее, чемmutable val
.