HashSet basiert auf HashMap.
Wenn wir uns die Implementierung von HashSet<E>
ansehen, wird alles unter HashMap<E,Object>
verwaltet.
<E>
wird als Schlüssel für HashMap
verwendet.
Und wir wissen, dass HashMap
nicht threadsicher ist. Deshalb haben wir ConcurrentHashMap
in Java.
Aufgrund dessen bin ich verwirrt darüber, dass warum wir kein ConcurrentHashSet haben, das auf dem ConcurrentHashMap
basieren sollte?
Fehlt mir noch etwas? Ich muss Set
in einer Umgebung mit mehreren Threads verwenden.
Wenn ich mein eigenes ConcurrentHashSet
erstellen möchte, kann ich es dann erreichen, indem ich HashMap
durch ConcurrentHashMap
ersetze und den Rest so lasse, wie er ist?
Es gibt keinen eingebauten Typ für ConcurrentHashSet
, da Sie jederzeit einen Satz von einer Karte ableiten können . Da es viele Arten von Karten gibt, verwenden Sie eine Methode, um einen Satz aus einer bestimmten Karte (oder Kartenklasse) zu erstellen.
Vor Java 8 erstellen Sie mithilfe von Collections.newSetFromMap(map)
ein Concurrent-Hash-Set, das von einer Concurrent-Hash-Map unterstützt wird
In Java 8 (von @Matt hervorgehoben) können Sie eine gleichzeitige Hash-Menge anzeigen über ConcurrentHashMap.newKeySet()
. Dies ist etwas einfacher als das alte newSetFromMap
, bei dem Sie ein leeres Kartenobjekt übergeben mussten. Aber es ist spezifisch für ConcurrentHashMap
.
Wie auch immer, die Entwickler von Java hätten jedes Mal, wenn eine neue Kartenschnittstelle erstellt wurde, eine neue Set-Schnittstelle erstellen können, aber dieses Muster wäre unmöglich durchzusetzen, wenn Dritte ihre eigenen Karten erstellen. Es ist besser, die statischen Methoden zu haben, die neue Mengen ableiten. Dieser Ansatz funktioniert immer, auch wenn Sie eigene Kartenimplementierungen erstellen.
Set<String> mySet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
Mit Guave 15 können Sie auch einfach verwenden:
Set s = Sets.newConcurrentHashSet();
Wie Ray Toal erwähnt ist es so einfach wie:
Set<String> myConcurrentSet = ConcurrentHashMap.newKeySet();
Es sieht so aus, als ob Java eine gleichzeitige Set-Implementierung mit ConcurrentSkipListSet bereitstellt. Ein SkipList Set ist nur eine spezielle Art von Set-Implementierung. Es implementiert weiterhin die Schnittstellen Serializable, Cloneable, Iterable, Collection, NavigableSet, Set und SortedSet. Dies könnte für Sie funktionieren, wenn Sie nur die Set-Schnittstelle benötigen.
Wie durch this gezeigt, ist der beste Weg, ein paralleles HashSet zu erhalten, mittels Collections.synchronizedSet()
Set s = Collections.synchronizedSet(new HashSet(...));
Das hat bei mir funktioniert und ich habe niemanden gesehen, der wirklich darauf hingewiesen hat.
EDIT Dies ist weniger effizient als die derzeit empfohlene Lösung, wie Eugene betont, da es Ihr Set nur in einen synchronisierten Dekorateur einwickelt, während ein ConcurrentHashMap
implementiert tatsächlich eine Nebenläufigkeit auf niedriger Ebene und kann Ihr Set genauso gut unterstützen. Vielen Dank an Herrn Stepanenkov für die Klarstellung.
http://docs.Oracle.com/javase/8/docs/api/Java/util/Collections.html#synchronizedSet-Java.util.Set-
Sie können guava's Sets.newSetFromMap(map)
verwenden, um eine zu erhalten. Java 6 hat diese Methode auch in Java.util.Collections
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;
}
}
Warum nicht: CopyOnWriteArraySet aus Java.util.concurrent verwenden?