36

The ScalaDoc says this about concurrentMap: "Deprecated (Since version 2.10.0) Use scala.collection.concurrent.Map instead." Unfortunately, the rest of the Scala docs has not been updated and still references concurrentMap.

I tried to mix in concurrent.Map into a HashMap, with the following results:

scala> val mmap = new mutable.HashMap[String, String] with collection.concurrent.Map[String, String]
<console>:16: error: object creation impossible, since:
it has 4 unimplemented members.
/** As seen from anonymous class $anon, the missing signatures are as follows.
 *  For convenience, these are usable as stub implementations.
 */
  def putIfAbsent(k: String,v: String): Option[String] = ???
  def remove(k: String,v: String): Boolean = ???
  def replace(k: String,v: String): Option[String] = ???
  def replace(k: String,oldvalue: String,newvalue: String): Boolean = ???

       val mmap = new mutable.HashMap[String, String] with collection.concurrent.Map[String, String]

So we see that instead of a simple mixin, some methods must also be implemented. Is this the best way to use concurrent.Map, or is there a better way?

5 Answers 5

52

The scala.collection.concurrent.Map trait is not meant to be mixed-in with an existing mutable Scala Map to obtain a thread-safe version of the map instance. The SynchronizedMap mixin existed for this purpose before 2.11, but is now deprecated.

Currently, Scala has the scala.collection.concurrent.TrieMap implementation for the scala.collection.concurrent.Map interface, but can wrap Java classes as well.

The scala.collection.concurrent.Map, in versions prior to 2.10 known as scala.collection.mutable.ConcurrentMap, interface is used when you:

  • want to implement your own concurrent, thread-safe Map from scratch

  • want to wrap an existing Java concurrent map implementation:

E.g:

import scala.collection._
import scala.collection.convert.decorateAsScala._
import java.util.concurrent.ConcurrentHashMap

val map: concurrent.Map[String, String] = new ConcurrentHashMap().asScala
  • want to write generic code that works concurrent maps, and don't want to commit to a specific implementation:

E.g.:

import scala.collection._

def foo(map: concurrent.Map[String, String]) = map.putIfAbsent("", "")

foo(new concurrent.TrieMap)
foo(new java.util.concurrent.ConcurrentSkipListMap().asScala)
  • you could implement your own wrapper around a single-threaded mutable map implementation by using synchronized (but you would need to ensure that your program is accessing the mutable map only through this wrapper and never directly).

E.g.:

class MySynchronizedMap[K, V](private val underlying: mutable.Map[K, V])
extends concurrent.Map[K, V] {
  private val monitor = new AnyRef
  def putIfAbsent(k: K,v: V): Option[String] = monitor.synchronized {
    underlying.get(k) match {
      case s: Some[V] => s
      case None =>
        underlying(k) = v
        None
    }
  }
  def remove(k: K, v: V): Boolean = monitor.synchronized {
    underlying.get(k) match {
      case Some(v0) if v == v0 => underlying.remove(k); true
      case None => false
    }
  }
  // etc.
}
7
  • 3
    Scala 2.11 deprecates the synchronized traits
    – Mike Slinn
    Commented May 11, 2014 at 14:28
  • scala.collection.mutable.ConcurrentMap is also deprecated in favor of scala.collection.concurrent.Map; making this change messes up the remainder of the solution
    – Mike Slinn
    Commented Jun 12, 2014 at 18:45
  • Could you explain? You could just replace ConcurrentMap with concurrent.Map in the examples above, no?
    – axel22
    Commented Jun 12, 2014 at 18:48
  • 1
    I'm the author of both ConcurrentMap and the concurrent.Map trait, and I'm pretty sure it does work. Maybe there's some typo in the code above, but unless you tell me more about what doesn't work, I cannot help :(
    – axel22
    Commented Jun 13, 2014 at 7:28
  • 3
    Since 2.12 we should use import scala.collection.JavaConverters._, scala.collection.convert.decorateAsScala._ is deprecated
    – vasigorc
    Commented Mar 13, 2019 at 9:21
16

Unless you want to implement a concurrent mutable hash map yourself, you have to use scala.collection.concurrent.TrieMap.

2
  • 1
    What about concurrent HashSet and WeakHashMap?
    – Mike Slinn
    Commented Sep 6, 2013 at 16:44
  • 6
    I don't understand what you're asking. Commented Sep 6, 2013 at 19:40
10

Taken from this comment directly: https://stackoverflow.com/a/49689669/7082628

In 2018, apparently you can just do this:

import java.util.concurrent.ConcurrentHashMap

val m: ConcurrentHashMap[String,MyClass] = new ConcurrentHashMap
3

By "simple mixin", perhaps you're asking if the trait can be used as a decorator as shown here for SynchronizedMap, and the answer is apparently not.

Implementations include TrieMap and the wrapper for Java's ConcurrentMap (of which there are two implementations). (Java also offers ConcurrentSkipListSet as Set.)

Also see this roll-your-own question.

They have you covered on the conversion side of things, if that's what you were used to:

scala> import java.util.concurrent._
import java.util.concurrent._

scala> import collection.JavaConverters._
import collection.JavaConverters._

scala> val m = new ConcurrentHashMap[String, Int]
m: java.util.concurrent.ConcurrentHashMap[String,Int] = {}

scala> val mm = m.asScala
mm: scala.collection.concurrent.Map[String,Int] = Map()

scala> mm.replace("five",5)
res0: Option[Int] = None

scala> mm.getClass
res1: Class[_ <: scala.collection.concurrent.Map[String,Int]] = class scala.collection.convert.Wrappers$JConcurrentMapWrapper
1
  • I've seen this example written with infix notation elsewhere. What about concurrent versions of HashSet and WeakHashMap? Have they been implemented or is there a way to mix in behavior into the single-threaded version? I'd prefer to synchronize on individual keys, not synchronize the entire collection
    – Mike Slinn
    Commented Sep 7, 2013 at 6:34
3

Update for 2021 and Scala 2.13:

You need to use different implicit conversion when wrapping the Java concurrent map implementation:

import java.util.concurrent.ConcurrentHashMap
import scala.collection.concurrent
import scala.jdk.CollectionConverters._

val map: concurrent.Map[String, String] = new ConcurrentHashMap().asScala

Not the answer you're looking for? Browse other questions tagged or ask your own question.