Synchronized sets and maps
To get a thread-safe mutable map, you can mix the SynchronizedMap
trait trait into whatever particular map implementation you desire.
For example, you can mix SynchronizedMap into HashMap, as
shown in the code below. This example begins
with an import of two traits, Map and SynchronizedMap, and one
class, HashMap, from package scala.collection.mutable. The rest
of the example is the definition of singleton object MapMaker,
which declares one method, makeMap. The makeMap method declares
its result type to be a mutable map of string keys to string values.
import scala.collection.mutable.{Map, |
SynchronizedMap, HashMap} |
object MapMaker { |
def makeMap: Map[String, String] = { |
new HashMap[String, String] with |
SynchronizedMap[String, String] { |
override def default(key: String) = |
"Why do you want to know?" |
} |
} |
}
|
Mixing in the SynchronizedMap trait.
The first statement inside the body of makeMap constructs a new mutable HashMap that mixes in the
SynchronizedMap trait:
new HashMap[String, String] with |
SynchronizedMap[String, String]
|
Given this code, the Scala compiler will generate a synthetic subclass of HashMap that mixes in SynchronizedMap, and create
(and return) an instance of it. This synthetic class will also override a method named default, because of this code:
override def default(key: String) = |
"Why do you want to know?"
|
If you ask a map to give you the value for a particular key, but it doesn't have a mapping for that key, you'll by default get a NoSuchElementException.
If you define a new map class and override the default method, however, your new map will return the value returned by default when
queried with a non-existent key. Thus, the synthetic HashMap subclass generated by the compiler from the code
in the synchronized map code will return the somewhat curt response string, "Why do you want to know?", when queried with
a non-existent key.
Because the mutable map returned by the makeMap method mixes in the SynchronizedMap trait, it can be used by
multiple threads at once. Each access to the map will be synchronized. Here's an example of the map being used, by one thread,
in the interpreter:
scala> val capital = MapMaker.makeMap |
capital: scala.collection.mutable.Map[String,String] = Map() |
scala> capital ++ List("US" -> "Washington", |
"Paris" -> "France", "Japan" -> "Tokyo") |
res0: scala.collection.mutable.Map[String,String] = |
Map(Paris -> France, US -> Washington, Japan -> Tokyo) |
scala> capital("Japan") |
res1: String = Tokyo |
scala> capital("New Zealand") |
res2: String = Why do you want to know? |
scala> capital += ("New Zealand" -> "Wellington") |
scala> capital("New Zealand") |
res3: String = Wellington
|
You can create synchronized sets similarly to the way you create synchronized maps. For example, you could create a synchronized HashSet
by mixing in the
SynchronizedSet trait, like this:
import scala.collection.mutable |
val synchroSet = |
new mutable.HashSet[Int] with |
mutable.SynchronizedSet[Int]
|
Finally, if you are thinking of using synchronized collections, you may also wish to consider the concurrent collections
of java.util.concurrent instead.
!!!explain concurrent map!!!
Next: Concrete Immutable Collection Classes