5

What are the possible ways to make a HashSet thread safe? Saw some samples as given below.

var test = new mutable.HashSet[Long] with mutable.SynchronizedSet[Long]

SynchronizedSet is deprecated at present. Any suggestions or samples will be very much helpful.

2
  • Note that the API docs suggest what you could use instead of the deprecated SynchronizedSet.
    – Jesper
    Commented Dec 6, 2016 at 11:07
  • They suggest to use ConcurrentHashMap I am interested in Set
    – Prakash
    Commented Dec 6, 2016 at 11:08

2 Answers 2

18

As the API documentation of scala.collection.mutable.SynchronizedSet suggests, you can use java.util.concurrent.ConcurrentHashMap[A, Unit] instead.

If you want it to look like a Set instead of like a Map, then you can use java.util.Collections.newSetFromMap to add a wrapper around the Map to make it look like a Set:

def createSet[T]() = java.util.Collections.newSetFromMap(
  new java.util.concurrent.ConcurrentHashMap[T, java.lang.Boolean])

This will, however, return a Java Set. You can wrap this as a scala.collection.mutable.Set:

def createSet[T]() = {
  import scala.collection.JavaConverters._
  java.util.Collections.newSetFromMap(
    new java.util.concurrent.ConcurrentHashMap[T, java.lang.Boolean]).asScala
}

Now you can create a synchronized set with elements of a specific type, for example Long, like this:

val set = createSet[Long]
5
  • Is there a way to check if my mutable.Set[T] is the concurrent or basic version?
    – robert
    Commented May 3, 2019 at 22:52
  • I'm a little concerned that your mutable.Set is not actually concurrent
    – robert
    Commented May 3, 2019 at 22:56
  • @robert It's a wrapper around a Java ConcurrentHashMap. The wrapper doesn't do anything that makes it not-concurrent.
    – Jesper
    Commented May 4, 2019 at 22:13
  • Thanks @Jesper . I've added a new Q&A here which (inductively) proves the concurrency of sets created in this way. stackoverflow.com/questions/56013503/…
    – robert
    Commented May 6, 2019 at 23:20
  • 4
    Also, you can create it in this manner: java.util.concurrent.ConcurrentHashMap.newKeySet[Int]().asScala. A little more concise.
    – robert
    Commented May 6, 2019 at 23:20
0

Building upon Jespers solution scala thread safe HashSet:

val myConcurrentSet = new java.util.concurrent.ConcurrentHashMap[String, Boolean]().keySet(true)

This way you use only the keys of the Map, thus a Set. Also it's needed to configure the default value for all the values, here as true but could be false as well. Failing to set this default value for key additions, when adding a key the method will raise:

[info]   java.lang.UnsupportedOperationException:
[info]   at java.base/java.util.concurrent.ConcurrentHashMap$KeySetView.add(ConcurrentHashMap.java:4651)

as v will be null in java.base/java.util.concurrent.ConcurrentHashMap.add(K e):

/**...
* Throws:
* ...
* UnsupportedOperationException – if no default mapped value for additions was provided
**/
public boolean add(K e) {
            V v;
            if ((v = value) == null)
                throw new UnsupportedOperationException();
            return map.putVal(e, v, true) == null;
        }

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