18.3. Kolekcje zmienne i niezmienne

Jedną z cech odróżniających listy od tablic jest to, że listy są przykładem kolekcji niezmiennych, a tablice są przykładem kolekcji zmiennych. Kolekcje niezmienne nie mogą być zmienione po utworzeniu, natomiast kolekcje zmienne mogą. W poniższym przykładzie utworzona zostaje tablica trójelementowa, a następnie jeden z jej elementów zostaje zmieniony. Dane w tablicy do której odwołuje się wartość b są inne przed i po zmianie.

scala> val b = Array(1,2,3)
b: Array[Int] = Array(1, 2, 3)

scala> b(1) = 5

scala> b
res1: Array[Int] = Array(1, 5, 3)

Podobna operacja nie jest możliwa w przypadku list.

scala> val a = List(1,2,3)
a: List[Int] = List(1, 2, 3)

scala> a(1) = 5
<console>:12: error: value update is not a member of List[Int]
       a(1) = 5
       ^

Pakiet scala.collection zawiera pakiety o nazwach immutable i mutable. Kolekcje zdefiniowane w pakiecie immutable są niezmienne. Kolekcje zdefiniowane w pakiecie mutable są zmienne. Wszystkie trzy rodzaje kolekcji omówione wcześniej (sekwencje, mapy, zbiory) mają wersje zmienne i niezmienne.

scala> val iSeq = scala.collection.immutable.Seq('a','b')
iSeq: scala.collection.immutable.Seq[Char] = List(a, b)

scala> val iMap = scala.collection.immutable.Map('a'->1,'b'->2)
iMap: scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 2)

scala> val iSet = scala.collection.immutable.Set('a','b')
iSet: scala.collection.immutable.Set[Char] = Set(a, b)

scala> val mSeq = scala.collection.mutable.Seq('a','b')
mSeq: scala.collection.mutable.Seq[Char] = ArrayBuffer(a, b)

scala> val mMap = scala.collection.mutable.Map('a'->1,'b'->2)
mMap: scala.collection.mutable.Map[Char,Int] = Map(b -> 2, a -> 1)

scala> val mSet = scala.collection.mutable.Set('a','b')
mSet: scala.collection.mutable.Set[Char] = Set(a, b)

Zmienne wersje sekwencji, zbiorów i map posiadają metodę o nazwie update, która ma dwa parametry, z których pierwszy ma typ taki, jak typ parametru metody apply, a drugi ma typ taki, jak typ danych zwracany przez tę metodę. Za pomocą metody update możemy zmieniać zawartość kolekcji. Metodę update można wywoływać niejawnie za pomocą składni przypisania, jak to zostało opisane w punkcie 7.12.

scala> mSeq(1) = 'd'

scala> mSeq
res4: scala.collection.mutable.Seq[Char] = ArrayBuffer(a, d)
scala> mMap('b') = 4

scala> mMap
res6: scala.collection.mutable.Map[Char,Int] = Map(b -> 4, a -> 1)

scala> mSet('b') = false

scala> mSet
res8: scala.collection.mutable.Set[Char] = Set(a)

Za pomocą metody update można dodać nowe elementy do zbiorów i map, ale nie do sekwencji.

scala> mSeq(2) = 'f'
java.lang.IndexOutOfBoundsException: 2
  at scala.collection.mutable.ResizableArray$class.update(ResizableArray.scala:48)
  at scala.collection.mutable.ArrayBuffer.update(ArrayBuffer.scala:48)
  ... 33 elided

scala> mMap('b') = 8

scala> mMap
res11: scala.collection.mutable.Map[Char,Int] = Map(b -> 8, a -> 1)

scala> mSet('d') = true

scala> mSet
res13: scala.collection.mutable.Set[Char] = Set(d, a)

W przypadku sekwencji i map niezmiennych możemy skorzystać metody updated, która różni się od metody update tym, że nie zmienia kolekcji, ale zwraca nową. W przypadku zbiorów metoda updated nie jest dostępna.

scala> val iSeq2 = iSeq.updated(1,'d')
iSeq2: scala.collection.immutable.Seq[Char] = List(a, d)

scala> iSeq
res14: scala.collection.immutable.Seq[Char] = List(a, b)

scala> val iMap2 = iMap.updated('b', 4)
iMap2: scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 4)

scala> iMap
res15: scala.collection.immutable.Map[Char,Int] = Map(a -> 1, b -> 2)

scala> val iSet2 = iSet.updated('b',false)
<console>:11: error: value updated is not a member of scala.collection.immutable.Set[Char]
       val iSet2 = iSet.updated('b',false)
                        ^

W pakiecie scala.collection znajdują się definicje cech, z których dziedziczą zarówno kolekcje z podpakietów immutable, jak i mutable. Jeśli odwołujemy się do kolekcji za pomocą jednego z typów z tego pakietu, to nawet, jeśli nie mamy możliwości samodzielnej zmiany kolekcji, nie oznacza to, że kolekcja nie może zostać zmieniona za pomocą innego odwołania do niej.

W poniższym przykładzie tworzona jest kolekcja zmienna, do której odwołania przechowują wartości a i b.

scala> val a = scala.collection.mutable.Seq(1,2,3,4,5)
a: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2, 3, 4, 5)
scala> val b: scala.collection.Seq[Int] = a
b: Seq[Int] = ArrayBuffer(1, 2, 3, 4, 5)

Wartość a ma typ scala.collection.mutable.Seq[Int] i umożliwia dokonywanie zmian w kolekcji, na przykład za pomocą metody update.

scala> a(2) = 8

scala> a
res17: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2, 8, 4, 5)

Wartość b ma typ scala.collection.Seq[Int] i przy jej pomocy nie można wywołać metody update.

scala> b(2) = 9
<console>:13: error: value update is not a member of Seq[Int]
       b(2) = 9
       ^

scala> b
res19: Seq[Int] = ArrayBuffer(1, 2, 8, 4, 5)

Język programowania Scala Wydanie 2. Copyright © Grzegorz Balcerek 2016

Licencja Creative Commons

Ten utwór jest dostępny na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowe.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.