Collections

Изменяемые и Неизменяемые Коллекции

Language

В коллекциях Scala постоянно проводят различие между неизменяемыми и изменяемыми коллекциями. Изменяемые (mutable) коллекции могут быть изменены или дополнены. Это означает, что вы можете изменять, добавлять или удалять её элементы. Неизменяемые (Immutable) коллекции, напротив, никогда не меняются. У них есть операции, имитирующие добавления, удаления или обновления, но эти операции каждый раз будут возвращать новую коллекцию и оставлять старую коллекцию без изменений.

Все варианты коллекции находятся в пакете scala.collection либо в одном из его подпакетов mutable или immutable. Большинство коллекции, которые необходимы для работы с клиентским кодом, существуют в трех вариантах, те которые находятся в пакетах scala.collection, scala.collection.immutable или scala collection.mutable. У каждого варианта свои особенности в работе, связанные с разным подходом к обработке изменений.

Каждая коллекция в пакете scala.collection.immutable гарантированно будет неизменяемой. Такая коллекция никогда не изменится после ее создания. Поэтому, вы, можете положиться на факт того, что повторный доступ к значениям коллекции в любой момент времени приведет к одному и томуже результату.

Известно, что у коллекции в пакете scala.collection.mutable есть операции, которые изменяют саму коллекцию. Поэтому, работая с изменяемой коллекцией вам нужно четко понимать, где и когда в нее вносятся изменения.

Коллекция в пакете scala.collection может быть как изменяемой, так и неизменяемой. Например, collection.IndexedSeq[T] является базовой для обоих коллекций collection.immutable.IndexedSeq[T] и collection.mutable.IndexedSeq[T] Как правило, базовые коллекции пакета scala.collection поддерживают операции преобразования, затрагивающие всю коллекцию, неизменяемые коллекции пакета scala.collection.immutable обычно добавляют операции добавления или удаления отдельных элементов, а изменяемые коллекции пакета scala.collection.mutable обычно добавляют к базовому интерфейсу операции модификации элементов, основанные на побочных эффектах.

Еще одним отличием базовой коллекции от неизменяемой является то, что пользователи неизменяемой коллекции имеют гарантию, что никто не сможет изменить коллекцию, а пользователи базовой коллекции лишь обещают не менять ее самостоятельно. Даже если тип такой коллекции не предоставляет никаких операций для модификации коллекции, все равно возможно, что эта коллекция, может быть изменена какими-либо сторонними пользователями.

По умолчанию Scala всегда выбирает неизменяемые коллекции. Например, если вы просто пишете Set без префикса или импортируете Set откуда-то, вы получаете неизменяемый Set, а если вы пишете Iterable - получите неизменяемую Iterable коллекцию, потому что такие связки прописаны по умолчанию, в пакетеscala. Чтобы получить изменяемую версию необходимо явно написать collection.mutable.Set или collection.mutable.Iterable.

Полезное соглашение, если вы хотите использовать как изменяемую, так и неизменяемую версию коллекций - импортируйте только пакет collection.mutable.

import scala.collection.mutable

Тогда указание типа Set без префикса по-прежнему будет относиться к неизменяемой коллекции, в то время как mutable.Set буде относиться к переменному аналогу.

Последним пакетом в иерархии коллекций является scala.collection.generic. Этот пакет содержит строительные элементы для абстрагирования поверх конкретных коллекций.

Для удобства и обратной совместимости некоторые важные типы имеют псевдонимы в scala пакете, поэтому вы можете использовать их указывая обычное имя без необходимости импорта пакета. Примером может служить тип List, к которому можно получить доступ следующим образом:

scala.collection.immutable.List   // Полное объявление
scala.List                        // объявление через псевдоним
List                              // т.к. scala._ всегда автоматически импортируется
                                  // можно просто указать имя коллекции

Другие псевдонимы для типов Iterable, Seq, IndexedSeq, Iterator, LazyList, Vector, StringBuilder, и Range.

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

Иерархия базовых коллекций

На следующем рисунке показаны все коллекции из пакета scala.collection.immutable.

Иерархия неизменяемых коллекций

И на конец все коллекции из пакета scala.collection.mutable.

Иерархия изменяемых коллекций

Описания:

Граф описаний

Обзор API коллекций

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

Iterable("x", "y", "z")
Map("x" -> 24, "y" -> 25, "z" -> 26)
Set(Color.red, Color.green, Color.blue)
SortedSet("hello", "world")
Buffer(x, y, z)
IndexedSeq(1.0, 2.0)
LinearSeq(a, b, c)

Этот же принцип применяется и к конкретным реализациям коллекций, таким как

List(1, 2, 3)
HashMap("x" -> 24, "y" -> 25, "z" -> 26)

Все эти коллекции выводятся с помощью toString.

Все коллекции поддерживают API, предоставляемый Iterable, но с определенной спецификой на разных типах, где это имеет смысл. Например, метод map в классе Iterable возвращает в результате другой Iterable. Но в подклассах тип результата переопределяется. Например, вызов map в List снова дает List, вызов наSet снова дает Set и так далее.

scala> List(1, 2, 3) map (_ + 1)
res0: List[Int] = List(2, 3, 4)
scala> Set(1, 2, 3) map (_ * 2)
res0: Set[Int] = Set(2, 4, 6)

Такое поведение, повсеместно реализованное для коллекций, называется принцип единообразного типа возвращаемого значения (uniform return type principles).

Большинство классов в иерархии коллекций существуют в трех вариантах: базовый, изменяемый и неизменяемый. Единственным исключением является трейт Buffer, который существует только в виде изменяемой коллекции.

Далее мы рассмотрим все эти классы поподробнее.

Contributors to this page: