Talha Ahmed Khan
Talha Ahmed Khan

Reputation: 15483

Why there is no ConcurrentHashSet against ConcurrentHashMap

HashSet is based on HashMap.

If we look at HashSet<E> implementation, everything is been managed under HashMap<E,Object>.

<E> is used as a key of HashMap.

And we know that HashMap is not thread safe. That is why we have ConcurrentHashMap in Java.

Based on this, I am confused that why we don't have a ConcurrentHashSet which should be based on the ConcurrentHashMap?

Is there anything else that I am missing? I need to use Set in a multi-threaded environment.

Also, If I want to create my own ConcurrentHashSet can I achieve it by just replacing the HashMap to ConcurrentHashMap and leaving the rest as is?

Upvotes: 717

Views: 341790

Answers (11)

pizzi
pizzi

Reputation: 31

Imho what we really need is a common ConcurrentSet interface which should be on par with ConcurrentMap interface.

All existing code should align on this new interface to clarify a bit when we have a Set that is concurrent (thread-safe) or not...

for example:

ConcurrentSet<String> set = ConcurrentHashMap.newKeySet();
ConcurrentSet<String> set = Collections.synchronizedSet(new HashSet<>());
ConcurrentSet<String> set = new ConcurrentSkipListSet<>();

Upvotes: 0

Serge Mask
Serge Mask

Reputation: 1411

Prior to Java 8:

Set<String> mySet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());

Java 8 or greater:

ConcurrentHashMap.KeySetView<String, Boolean> mySet = ConcurrentHashMap.newKeySet();

Upvotes: 133

Sart
Sart

Reputation: 9

We have ConcurrentHashSet in io.vertx which can be used against java.util ConcurrentHashMap. https://www.javadoc.io/static/io.vertx/vertx-core/3.0.0/io/vertx/core/impl/ConcurrentHashSet.html

Upvotes: 0

Shendor
Shendor

Reputation: 787

Why not use: CopyOnWriteArraySet from java.util.concurrent?

Upvotes: 1

BullyWiiPlaza
BullyWiiPlaza

Reputation: 19243

Like Ray Toal mentioned it is as easy as:

Set<String> myConcurrentSet = ConcurrentHashMap.newKeySet();

Upvotes: 90

Nirro
Nirro

Reputation: 769

As pointed by this the best way to obtain a concurrency-able HashSet is by means of Collections.synchronizedSet()

Set s = Collections.synchronizedSet(new HashSet(...));

This worked for me and I haven't seen anybody really pointing to it.

EDIT This is less efficient than the currently aproved solution, as Eugene points out, since it just wraps your set into a synchronized decorator, while a ConcurrentHashMap actually implements low-level concurrency and it can back your Set just as fine. So thanks to Mr. Stepanenkov for making that clear.

http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedSet-java.util.Set-

Upvotes: 21

Ray Toal
Ray Toal

Reputation: 88478

There's no built in type for ConcurrentHashSet because you can always derive a set from a map. Since there are many types of maps, you use a method to produce a set from a given map (or map class).

Prior to Java 8, you produce a concurrent hash set backed by a concurrent hash map, by using Collections.newSetFromMap(map)

In Java 8 (pointed out by @Matt), you can get a concurrent hash set view via ConcurrentHashMap.newKeySet(). This is a bit simpler than the old newSetFromMap which required you to pass in an empty map object. But it is specific to ConcurrentHashMap.

Anyway, the Java designers could have created a new set interface every time a new map interface was created, but that pattern would be impossible to enforce when third parties create their own maps. It is better to have the static methods that derive new sets; that approach always works, even when you create your own map implementations.

Upvotes: 765

MD. Mohiuddin Ahmed
MD. Mohiuddin Ahmed

Reputation: 2182

import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;


public class ConcurrentHashSet<E> extends AbstractSet<E> implements Set<E>{
   private final ConcurrentMap<E, Object> theMap;

   private static final Object dummy = new Object();

   public ConcurrentHashSet(){
      theMap = new ConcurrentHashMap<E, Object>();
   }

   @Override
   public int size() {
      return theMap.size();
   }

   @Override
   public Iterator<E> iterator(){
      return theMap.keySet().iterator();
   }

   @Override
   public boolean isEmpty(){
      return theMap.isEmpty();
   }

   @Override
   public boolean add(final E o){
      return theMap.put(o, ConcurrentHashSet.dummy) == null;
   }

   @Override
   public boolean contains(final Object o){
      return theMap.containsKey(o);
   }

   @Override
   public void clear(){
      theMap.clear();
   }

   @Override
   public boolean remove(final Object o){
      return theMap.remove(o) == ConcurrentHashSet.dummy;
   }

   public boolean addIfAbsent(final E o){
      Object obj = theMap.putIfAbsent(o, ConcurrentHashSet.dummy);
      return obj == null;
   }
}

Upvotes: 8

kichik
kichik

Reputation: 34744

With Guava 15 you can also simply use:

Set s = Sets.newConcurrentHashSet();

Upvotes: 103

Mike Pone
Mike Pone

Reputation: 19330

It looks like Java provides a concurrent Set implementation with its ConcurrentSkipListSet. A SkipList Set is just a special kind of set implementation. It still implements the Serializable, Cloneable, Iterable, Collection, NavigableSet, Set, SortedSet interfaces. This might work for you if you only need the Set interface.

Upvotes: 23

Bozho
Bozho

Reputation: 597402

You can use guava's Sets.newSetFromMap(map) to get one. Java 6 also has that method in java.util.Collections

Upvotes: 15

Related Questions