Code Examples for

Programming in Scala, Fifth 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)
List(1, 2, 3).map(_ + 1) // List(2, 3, 4): List[Int] Set(1, 2, 3).map(_ * 2) // Set(2, 4, 6): Set[Int]

24.3 Trait Iterable


def iterator: Iterator[A]
// In file collections-in-depth/iterater-example-1.scala val xs = List(1, 2, 3, 4, 5) val git = xs.grouped(3) // an Iterator[List[Int]] git.next() // List(1, 2, 3) git.next() // List(4, 5) val sit = xs.sliding(3) // an Iterator[List[Int]] sit.next() // List(1, 2, 3) sit.next() // List(2, 3, 4) sit.next() // List(3, 4, 5)

24.4 Sets


// In file collections-in-depth/set-example-1.scala val fruit = Set("apple", "orange", "peach", "banana") fruit("peach") // true fruit("potato") // false
// In file collections-in-depth/set-example-2.scala var s = Set(1, 2, 3) s += 4 s -= 2 s // Set(1, 3, 4)
// In file collections-in-depth/set-example-3.scala val s = collection.mutable.Set(1, 2, 3) s += 4 // Set(1, 2, 3, 4) s -= 2 // Set(1, 3, 4) s // Set(1, 3, 4)

24.5 Maps


def get(key): Option[Value]
// In file collections-in-depth/f.scala def f(x: String) = println("taking my time.") Thread.sleep(100) x.reverse
// In file collections-in-depth/cachedF.scala val cache = collection.mutable.Map[String, String]()
scala> def cachedF(s: String) = cache.getOrElseUpdate(s, f(s)) def cachedF(s: String): String scala> cachedF("abc") taking my time. val res16: String = cba scala> cachedF("abc") val res17: String = cba
// In file collections-in-depth/cachedF.scala 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 val str: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
scala> def fibFrom(a: Int, b: Int): LazyList[Int] = | a #:: fibFrom(b, a + b) def fibFrom: (a: Int, b: Int)LazyList[Int]
scala> val fibs = fibFrom(1, 1).take(7) val fibs: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>) scala> fibs.toList val res23: List[Int] = List(1, 1, 2, 3, 5, 8, 13)
val vec = scala.collection.immutable.Vector.empty val vec2 = vec :+ 1 :+ 2 // Vector(1, 2) val vec3 = 100 +: vec2 // Vector(100, 1, 2) vec3(0) // 100
val vec = Vector(1, 2, 3) vec.updated(2, 4) // Vector(1, 2, 4) vec // Vector(1, 2, 3)
collection.immutable.IndexedSeq(1, 2, 3) // Vector(1, 2, 3)
// In file collections-in-depth/immutable-queues.scala val empty = scala.collection.immutable.Queue[Int]()
// In file collections-in-depth/immutable-queues.scala val has1 = empty.enqueue(1) // Queue(1)
// In file collections-in-depth/immutable-queues.scala val has123 = has1.enqueueAll(List(2, 3)) // Queue(1, 2, 3)
scala> val (element, has23) = has123.dequeue val element: Int = 1 has23: scala.collection.immutable.Queue[Int] = Queue(2, 3)
// In file collections-in-depth/range-example-1.scala 1 to 3 // Range(1, 2, 3) 5 to 14 by 3 // Range(5, 8, 11, 14)
// In file collections-in-depth/range-example-2.scala 1 until 3 // Range(1, 2)
// In file collections-in-depth/red-black-trees-example.scala val set = collection.immutable.TreeSet.empty[Int] set + 1 + 3 + 3 // TreeSet(1, 3)
// In file collections-in-depth/bitset-example.scala val bits = scala.collection.immutable.BitSet.empty val moreBits = bits + 3 + 4 + 4 // BitSet(3, 4) moreBits(3) // true moreBits(0) // false
import scala.collection.immutable.VectorMap val vm = VectorMap.empty[Int, String] val vm1 = vm + (1 -> "one") // VectorMap(1 -> one) val vm2 = vm1 + (2 -> "two") // VectorMap(1 -> one, 2 -> two) vm2 == Map(2 -> "two", 1 -> "one") // true
// In file collections-in-depth/listmap-example.scala val map = collection.immutable.ListMap(1 -> "one", 2 -> "two") map(2) // "two"

24.7 Concrete mutable collection classes


// In file collections-in-depth/arraybuffer-example.scala val buf = collection.mutable.ArrayBuffer.empty[Int] buf += 1 // ArrayBuffer(1) buf += 10 // ArrayBuffer(1, 10) buf.toArray // Array(1, 10)
// In file collections-in-depth/listbuffer-example.scala val buf = collection.mutable.ListBuffer.empty[Int] buf += 1 // ListBuffer(1) buf += 10 // ListBuffer(1, 10) buf.toList // List(1, 10)
val buf = new StringBuilder buf += 'a' // a buf ++= "bcdef" // abcdef buf.toString // abcdef
// In file collections-in-depth/queue-example-1.scala val queue = new scala.collection.mutable.Queue[String] queue += "a" // Queue(a) queue ++= List("b", "c") // Queue(a, b, c) queue // Queue(a, b, c) queue.dequeue // a queue // Queue(b, c)
// In file collections-in-depth/queue-example-2.scala val stack = new scala.collection.mutable.Stack[Int] stack.push(1) // Stack(1) stack // Stack(1) stack.push(2) // Stack(2, 1) stack // Stack(2, 1) stack.top // 2 stack // Stack(2, 1) stack.pop // 2 stack // Stack(1)
// In file collections-in-depth/hashtable-example.scala val map = collection.mutable.HashMap.empty[Int,String] map += (1 -> "make a web site") // Map(1 -> make a web site) map += (3 -> "profit!") // Map(1 -> make a web site, 3 -> profit!) map(1) // make a web site map.contains(2) // false
// In file collections-in-depth/mutable-bitset-example.scala val bits = scala.collection.mutable.BitSet.empty bits += 1 // BitSet(1) bits += 3 // BitSet(1, 3) bits // BitSet(1, 3)

24.8 Arrays


// In file collections-in-depth/array-example-1.scala val a1 = Array(1, 2, 3) val a2 = a1.map(_ * 3) // Array(3, 6, 9) val a3 = a2.filter(_ % 2 != 0) // Array(3, 9) a3.reverse // Array(9, 3)
// In file collections-in-depth/array-example-1.scala val seq: collection.Seq[Int] = a1 // ArraySeq(1, 2, 3) val a4: Array[Int] = seq.toArray // Array(1, 2, 3) a1 eq a4 // false
// In file collections-in-depth/array-example-2.scala val seq: collection.Seq[Int] = a1 // ArraySeq(1, 2, 3) seq.reverse // ArraySeq(3, 2, 1) val ops: collection.ArrayOps[Int] = a1 // Array(1, 2, 3) ops.reverse // Array(3, 2, 1)
// In file collections-in-depth/array-example-3.scala a1.reverse // Array(3, 2, 1)
// In file collections-in-depth/array-example-3.scala intArrayOps(a1).reverse // 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 do arr(i / 2) = xs(i) arr
2 | val arr = new Array[T]((xs.length + 1) / 2) | ^ | No ClassTag available for T
// In file collections-in-depth/evenElems.scala // 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 do arr(i / 2) = xs(i) arr
// In file collections-in-depth/evenElems.scala evenElems(Vector(1, 2, 3, 4, 5)) // Array(1, 3, 5) evenElems(Vector("this", "is", "a", "test", "run")) // Array(this, a, run)
scala> def wrap[U](xs: Vector[U]) = evenElems(xs) 1 |def wrap[U](xs: Vector[U]) = evenElems(xs) | ^ | No ClassTag available for U
// In file collections-in-depth/evenElems.scala def wrap[U: ClassTag](xs: Vector[U]) = evenElems(xs)

24.9 Strings


// In file collections-in-depth/string-example.scala val str = "hello" str.reverse // olleh str.map(_.toUpper) // HELLO str.drop(3) // lo str.slice(1, 4) // ell val s: Seq[Char] = str // hello

24.10 Performance characteristics

24.11 Equality


// In file collections-in-depth/equality-example.scala import collection.mutable.{HashMap, ArrayBuffer} val buf = ArrayBuffer(1, 2, 3) val map = HashMap(buf -> 3) // Map((ArrayBuffer(1, 2, 3),3)) map(buf) // 3 buf(0) += 1 map(buf) // java.util.NoSuchElementException: key not found: // ArrayBuffer(2, 2, 3)

24.12 Views


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

24.13 Iterators


// In file collections-in-depth/iterator-example-2.scala while it.hasNext do println(it.next())
// In file collections-in-depth/iterator-example-2.scala it.foreach(println)
// In file collections-in-depth/iterator-example-2.scala for elem <- it do println(elem)
// In file collections-in-depth/iterator-example-3.scala val it = Iterator("a", "number", "of", "words") val lit = it.map(_.length) it.hasNext // true lit.foreach(println) // prints 1, 6, 2, 5 it.hasNext // false
// In file collections-in-depth/iterator-example-4.scala val it = Iterator("a", "number", "of", "words") val dit = it.dropWhile(_.length < 2) dit.next() // number it.next() // of
// In file collections-in-depth/iterator-example-5.scala val (it1, it2) = it.duplicate
// This won't work def skipEmptyWordsNOT(it: Iterator[String]) = while it.next().isEmpty do {}
// In file collections-in-depth/skipEmptyWords.scala def skipEmptyWords(it: BufferedIterator[String]) = while it.head.isEmpty do it.next()
// In file collections-in-depth/iterator-buffered.scala val it = Iterator(1, 2, 3, 4) val bit = it.buffered bit.head // 1 bit.next() // 1 bit.next() // 2

24.14 Creating collections from scratch


// In file collections-in-depth/collection-from-scratch.scala 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
// In file collections-in-depth/list-apply.scala List.apply(1.0, 2.0)
scala> List(1, 2, 3) val res17: List[Int] = List(1, 2, 3) scala> Iterable(1, 2, 3) val res18: Iterable[Int] = List(1, 2, 3) scala> mutable.Iterable(1, 2, 3) val 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 jdk.CollectionConverters.*
scala> import collection.mutable.* scala> val jul: java.util.List[Int] = ArrayBuffer(1, 2, 3).asJava val jul: java.util.List[Int] = [1, 2, 3] scala> val buf: Seq[Int] = jul.asScala val 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) val 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, Fifth Edition (the "Stairway Book"), please visit:

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

and:

http://booksites.artima.com/programming_in_scala_5ed

Copyright © 2007-2020 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.