Code Examples for

Programming in Scala, Fourth Edition

Return to chapter index

24 Collections in Depth

Sample run of chapter's interpreter examples

24.1 Mutable and immutable collections

24.2 Collections consistency


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)
scala> List(1, 2, 3) map (_ + 1) res0: List[Int] = List(2, 3, 4) scala> Set(1, 2, 3) map (_ * 2) res1: scala.collection.immutable.Set[Int] = Set(2, 4, 6)

24.3 Trait Iterable


def iterator: Iterator[A]
scala> val xs = List(1, 2, 3, 4, 5) xs: List[Int] = List(1, 2, 3, 4, 5) scala> val git = xs grouped 3 git: Iterator[List[Int]] = non-empty iterator scala> git.next() res2: List[Int] = List(1, 2, 3) scala> git.next() res3: List[Int] = List(4, 5) scala> val sit = xs sliding 3 sit: Iterator[List[Int]] = non-empty iterator scala> sit.next() res4: List[Int] = List(1, 2, 3) scala> sit.next() res5: List[Int] = List(2, 3, 4) scala> sit.next() res6: List[Int] = List(3, 4, 5)

24.4 Sets


scala> val fruit = Set("apple", "orange", "peach", | "banana") fruit: scala.collection.immutable.Set[String] = Set(apple, orange, peach, banana) scala> fruit("peach") res7: Boolean = true scala> fruit("potato") res8: Boolean = false
scala> var s = Set(1, 2, 3) s: scala.collection.immutable.Set[Int] = Set(1, 2, 3) scala> s += 4; s -= 2 scala> s res10: scala.collection.immutable.Set[Int] = Set(1, 3, 4)
scala> val s = collection.mutable.Set(1, 2, 3) s: scala.collection.mutable.Set[Int] = Set(1, 2, 3) scala> s += 4 res11: s.type = Set(1, 2, 3, 4) scala> s -= 2 res12: s.type = Set(1, 3, 4)

24.5 Maps


def get(key): Option[Value]
scala> def f(x: String) = { | println("taking my time."); Thread.sleep(100) | x.reverse } f: (x: String)String
scala> val cache = collection.mutable.Map[String, String]() cache: scala.collection.mutable.Map[String,String] = Map()
scala> def cachedF(s: String) = cache.getOrElseUpdate(s, f(s)) cachedF: (s: String)String scala> cachedF("abc") taking my time. res16: String = cba scala> cachedF("abc") res17: String = cba
def cachedF(arg: String) = cache get arg match { case Some(result) => result case None => val result = f(arg) cache(arg) = result result }

24.6 Concrete immutable collection classes


scala> val str = 1 #:: 2 #:: 3 #:: LazyList.empty str: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
scala> def fibFrom(a: Int, b: Int): LazyList[Int] = | a #:: fibFrom(b, a + b) fibFrom: (a: Int, b: Int)LazyList[Int]
scala> val fibs = fibFrom(1, 1).take(7) fibs: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>) scala> fibs.toList res23: List[Int] = List(1, 1, 2, 3, 5, 8, 13)
scala> val vec = scala.collection.immutable.Vector.empty vec: scala.collection.immutable.Vector[Nothing] = Vector() scala> val vec2 = vec :+ 1 :+ 2 vec2: scala.collection.immutable.Vector[Int] = Vector(1, 2) scala> val vec3 = 100 +: vec2 vec3: scala.collection.immutable.Vector[Int] = Vector(100, 1, 2) scala> vec3(0) res24: Int = 100
scala> val vec = Vector(1, 2, 3) vec: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3) scala> vec updated (2, 4) res25: scala.collection.immutable.Vector[Int] = Vector(1, 2, 4) scala> vec res26: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3)
scala> collection.immutable.IndexedSeq(1, 2, 3) res27: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
scala> val empty = scala.collection.immutable.Queue[Int]() empty: scala.collection.immutable.Queue[Int] = Queue()
scala> val has1 = empty.enqueue(1) has1: scala.collection.immutable.Queue[Int] = Queue(1)
scala> val has123 = has1.enqueueAll(List(2, 3)) has123: scala.collection.immutable.Queue[Int] = Queue(1, 2, 3)
scala> val (element, has23) = has123.dequeue element: Int = 1 has23: scala.collection.immutable.Queue[Int] = Queue(2, 3)
scala> 1 to 3 res31: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3) scala> 5 to 14 by 3 res32: scala.collection.immutable.Range = Range(5, 8, 11, 14)
scala> 1 until 3 res33: scala.collection.immutable.Range = Range(1, 2)
scala> val set = collection.immutable.TreeSet.empty[Int] set: scala.collection.immutable.TreeSet[Int] = TreeSet() scala> set + 1 + 3 + 3 res34: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 3)
scala> val bits = scala.collection.immutable.BitSet.empty bits: scala.collection.immutable.BitSet = BitSet() scala> val moreBits = bits + 3 + 4 + 4 moreBits: scala.collection.immutable.BitSet = BitSet(3, 4) scala> moreBits(3) res35: Boolean = true scala> moreBits(0) res36: Boolean = false
scala> val vm = scala.collection.immutable.VectorMap. empty[Int, String] vm: scala.collection.immutable.VectorMap[Int,String] = VectorMap() scala> val vm1 = vm + (1 -> "one") vm1: scala.collection.immutable.VectorMap[Int,String] = VectorMap(1 -> one) scala> val vm2 = vm1 + (2 -> "two") vm2: scala.collection.immutable.VectorMap[Int,String] = VectorMap(1 -> one, 2 -> two) scala> vm2 == Map(2 -> "two", 1 -> "one") res29: Boolean = true
scala> val map = collection.immutable.ListMap( | 1 -> "one", 2 -> "two") map: scala.collection.immutable.ListMap[Int,String] = Map(1 -> one, 2 -> two) scala> map(2) res37: String = "two"

24.7 Concrete mutable collection classes


scala> val buf = collection.mutable.ArrayBuffer.empty[Int] buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer() scala> buf += 1 res38: buf.type = ArrayBuffer(1) scala> buf += 10 res39: buf.type = ArrayBuffer(1, 10) scala> buf.toArray res40: Array[Int] = Array(1, 10)
scala> val buf = collection.mutable.ListBuffer.empty[Int] buf: scala.collection.mutable.ListBuffer[Int] = ListBuffer() scala> buf += 1 res41: buf.type = ListBuffer(1) scala> buf += 10 res42: buf.type = ListBuffer(1, 10) scala> buf.toList res43: List[Int] = List(1, 10)
scala> val buf = new StringBuilder buf: StringBuilder = scala> buf += 'a' res44: buf.type = a scala> buf ++= "bcdef" res45: buf.type = abcdef scala> buf.toString res46: String = abcdef
scala> val queue = new scala.collection.mutable.Queue[String] queue: scala.collection.mutable.Queue[String] = Queue() scala> queue += "a" res47: queue.type = Queue(a) scala> queue ++= List("b", "c") res48: queue.type = Queue(a, b, c) scala> queue res49: scala.collection.mutable.Queue[String] = Queue(a, b, c) scala> queue.dequeue res50: String = a scala> queue res51: scala.collection.mutable.Queue[String] = Queue(b, c)
scala> val stack = new scala.collection.mutable.Stack[Int] stack: scala.collection.mutable.Stack[Int] = Stack() scala> stack.push(1) res52: stack.type = Stack(1) scala> stack res53: scala.collection.mutable.Stack[Int] = Stack(1) scala> stack.push(2) res54: stack.type = Stack(2, 1) scala> stack res55: scala.collection.mutable.Stack[Int] = Stack(2, 1) scala> stack.top res56: Int = 2 scala> stack res57: scala.collection.mutable.Stack[Int] = Stack(2, 1) scala> stack.pop res58: Int = 2 scala> stack res59: scala.collection.mutable.Stack[Int] = Stack(1)
scala> val map = collection.mutable.HashMap.empty[Int,String] map: scala.collection.mutable.HashMap[Int,String] = Map() scala> map += (1 -> "make a web site") res60: map.type = Map(1 -> make a web site) scala> map += (3 -> "profit!") res61: map.type = Map(1 -> make a web site, 3 -> profit!) scala> map(1) res62: String = make a web site scala> map contains 2 res63: Boolean = false
scala> val bits = scala.collection.mutable.BitSet.empty bits: scala.collection.mutable.BitSet = BitSet() scala> bits += 1 res64: bits.type = BitSet(1) scala> bits += 3 res65: bits.type = BitSet(1, 3) scala> bits res66: scala.collection.mutable.BitSet = BitSet(1, 3)

24.8 Arrays


scala> val a1 = Array(1, 2, 3) a1: Array[Int] = Array(1, 2, 3) scala> val a2 = a1 map (_ * 3) a2: Array[Int] = Array(3, 6, 9) scala> val a3 = a2 filter (_ % 2 != 0) a3: Array[Int] = Array(3, 9) scala> a3.reverse res1: Array[Int] = Array(9, 3)
scala> val seq: collection.Seq[Int] = a1 seq: scala.collection.Seq[Int] = ArraySeq(1, 2, 3) scala> val a4: Array[Int] = seq.toArray a4: Array[Int] = Array(1, 2, 3) scala> a1 eq a4 res2: Boolean = false
scala> val seq: collection.Seq[Int] = a1 seq: scala.collection.Seq[Int] = ArraySeq(1, 2, 3) scala> seq.reverse res2: scala.collection.Seq[Int] = ArraySeq(3, 2, 1) scala> val ops: collection.ArrayOps[Int] = a1 ops: scala.collection.ArrayOps[Int] = ... scala> ops.reverse res3: Array[Int] = Array(3, 2, 1)
scala> a1.reverse res4: Array[Int] = Array(3, 2, 1)
scala> intArrayOps(a1).reverse res5: Array[Int] = Array(3, 2, 1)
// This is wrong! def evenElems[T](xs: Vector[T]): Array[T] = { val arr = new Array[T]((xs.length + 1) / 2) for (i <- 0 until xs.length by 2) arr(i / 2) = xs(i) arr }
error: cannot find class tag for element type T val arr = new Array[T]((arr.length + 1) / 2) ^
// This works import scala.reflect.ClassTag def evenElems[T: ClassTag](xs: Vector[T]): Array[T] = { val arr = new Array[T]((xs.length + 1) / 2) for (i <- 0 until xs.length by 2) arr(i / 2) = xs(i) arr }
scala> evenElems(Vector(1, 2, 3, 4, 5)) res6: Array[Int] = Array(1, 3, 5) scala> evenElems(Vector("this", "is", "a", "test", "run")) res7: Array[java.lang.String] = Array(this, a, run)
scala> def wrap[U](xs: Vector[U]) = evenElems(xs) <console>:9: error: No ClassTag available for U def wrap[U](xs: Vector[U]) = evenElems(xs) ^
scala> def wrap[U: ClassTag](xs: Vector[U]) = evenElems(xs) wrap: [U](xs: Vector[U])(implicit evidence$1: scala.reflect.ClassTag[U])Array[U]

24.9 Strings


scala> val str = "hello" str: java.lang.String = hello scala> str.reverse res6: String = olleh scala> str.map(_.toUpper) res7: String = HELLO scala> str drop 3 res8: String = lo scala> str.slice(1, 4) res9: String = ell scala> val s: Seq[Char] = str s: Seq[Char] = hello

24.10 Performance characteristics

24.11 Equality


scala> import collection.mutable.{HashMap, ArrayBuffer} import collection.mutable.{HashMap, ArrayBuffer} scala> val buf = ArrayBuffer(1, 2, 3) buf: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3) scala> val map = HashMap(buf -> 3) map: scala.collection.mutable.HashMap[scala.collection. mutable.ArrayBuffer[Int],Int] = Map((ArrayBuffer(1, 2, 3),3)) scala> map(buf) res13: Int = 3 scala> buf(0) += 1 scala> map(buf) java.util.NoSuchElementException: key not found: ArrayBuffer(2, 2, 3)

24.12 Views


def lazyMap[T, U](coll: Iterable[T], f: T => U) = new Iterable[U] { def iterator = coll.iterator map f }
scala> val v = Vector(1 to 10: _*) v: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> v map (_ + 1) map (_ * 2) res5: scala.collection.immutable.Vector[Int] = Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22)
scala> (v.view map (_ + 1) map (_ * 2)).to(Vector) res12: Seq[Int] = Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22)
scala> val vv = v.view vv: scala.collection.IndexedSeqView[Int] = IndexedSeqView(<not computed>)
scala> vv map (_ + 1) res13: scala.collection.IndexedSeqView[Int] = IndexedSeqView(<not computed>)
scala> res13 map (_ * 2) res14: scala.collection.IndexedSeqView[Int] = IndexedSeqView(<not computed>)
scala> res14.to(Vector) res15: Seq[Int] = Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22)
def isPalindrome(x: String) = x == x.reverse def findPalindrome(s: Iterable[String]) = s find isPalindrome
findPalindrome(words take 1000000)
findPalindrome(words.view take 1000000)
val actors = for (i <- 1 to 10) yield actor { ... }
val actors = (1 to 10) map (i => actor { ... })
val actors = for (i <- (1 to 10).view) yield actor { ... }

24.13 Iterators


while (it.hasNext) println(it.next())
it foreach println
for (elem <- it) println(elem)
scala> val it = Iterator("a", "number", "of", "words") it: Iterator[java.lang.String] = <iterator> scala> it.map(_.length) res1: Iterator[Int] = <iterator> scala> it.hasNext res2: Boolean = true scala> res1 foreach println 1 6 2 5 scala> it.hasNext res4: Boolean = false
scala> val it = Iterator("a", "number", "of", "words") it: Iterator[java.lang.String] = <iterator> scala> it dropWhile (_.length < 2) res4: Iterator[java.lang.String] = <iterator> scala> it.next() res5: java.lang.String = number
val (it1, it2) = it.duplicate
// This won't work def skipEmptyWordsNOT(it: Iterator[String]) = { while (it.next().isEmpty) {} }
def skipEmptyWords(it: BufferedIterator[String]) = while (it.head.isEmpty) { it.next() }
scala> val it = Iterator(1, 2, 3, 4) it: Iterator[Int] = <iterator> scala> val bit = it.buffered bit: java.lang.Object with scala.collection. BufferedIterator[Int] = <iterator> scala> bit.head res10: Int = 1 scala> bit.next() res11: Int = 1 scala> bit.next() res11: Int = 2

24.14 Creating collections from scratch


Iterable() // An empty collection List() // The empty list List(1.0, 2.0) // A list with elements 1.0, 2.0 Vector(1.0, 2.0) // A vector with elements 1.0, 2.0 Iterator(1, 2, 3) // An iterator returning three integers. Set(dog, cat, bird) // A set of three animals HashSet(dog, cat, bird) // A hash set of the same animals Map('a' -> 7, 'b' -> 0) // A map from characters to integers
List.apply(1.0, 2.0)
scala> List(1, 2, 3) res17: List[Int] = List(1, 2, 3)
scala> Iterable(1, 2, 3) res18: Iterable[Int] = List(1, 2, 3) scala> mutable.Iterable(1, 2, 3) res19: scala.collection.mutable.Iterable[Int] = ArrayBuffer(1, 2, 3)

24.15 Conversions between Java and Scala collections


Iterator \Leftrightarrow java.util.Iterator Iterator \Leftrightarrow java.util.Enumeration Iterable \Leftrightarrow java.lang.Iterable Iterable \Leftrightarrow java.util.Collection mutable.Buffer \Leftrightarrow java.util.List mutable.Set \Leftrightarrow java.util.Set mutable.Map \Leftrightarrow java.util.Map
scala> import collection.JavaConverters._ import collection.JavaConverters._
scala> import collection.mutable._ import collection.mutable._ scala> val jul: java.util.List[Int] = ArrayBuffer(1, 2, 3).asJava jul: java.util.List[Int] = [1, 2, 3] scala> val buf: Seq[Int] = jul.asScala buf: scala.collection.mutable.Seq[Int] = ArrayBuffer(1, 2, 3) scala> val m: java.util.Map[String, Int] = HashMap("abc" -> 1, "hello" -> 2).asJava m: java.util.Map[String,Int] = {hello=2, abc=1}
Seq \Rightarrow java.util.List mutable.Seq \Rightarrow java.util.List Set \Rightarrow java.util.Set Map \Rightarrow java.util.Map
scala> val jul: java.util.List[Int] = List(1, 2, 3) jul: java.util.List[Int] = [1, 2, 3] scala> jul.add(7) java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:131)

24.16 Conclusion

For more information about Programming in Scala, Fourth Edition (the "Stairway Book"), please visit:

http://www.artima.com/shop/programming_in_scala_4ed

and:

http://booksites.artima.com/programming_in_scala_4ed

Copyright © 2007-2019 Artima, Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.