为什么没有ConcurrentHashSet对ConcurrentHashMap

HashSet基于HashMap。

如果我们看HashSet<E>实现,一切都在HashMap<E,Object>下管理。

<E>作为HashMap的键。

而且我们知道HashMap不是线程安全的。这就是为什么我们在Java中使用ConcurrentHashMap

基于此,我很困惑为什么我们没有一个ConcurrentHashSet应该基于ConcurrentHashMap?

我还遗漏了什么吗?我需要在多线程环境中使用Set

此外,如果我想创建自己的ConcurrentHashSet,我可以通过将HashMap替换为ConcurrentHashMap并保留其余部分来实现它吗?

296631 次浏览

你可以用番石榴的Sets.newSetFromMap(map)来得到一个。Java 6在java.util.Collections中也有这个方法

ConcurrentHashSet没有内置类型,因为您总是可以从映射中设置推导出。由于有许多类型的映射,您可以使用一个方法从给定的映射(或映射类)生成一个集合。

在Java 8之前,您可以使用Collections.newSetFromMap(map)生成一个并发散列集,该散列集由并发散列映射支持

在Java 8中(由@Matt指出),您可以通过ConcurrentHashMap.newKeySet()获得一个并发散列集视图。这比旧的newSetFromMap要简单一些,后者需要传入一个空的映射对象。但它是针对ConcurrentHashMap的。

无论如何,Java设计人员可以在每次创建新的映射接口时创建一个新的set接口,但是当第三方创建他们自己的映射时,这种模式是不可能强制执行的。最好使用派生新集合的静态方法;这种方法总是有效的,即使在创建自己的映射实现时也是如此。

看起来Java通过它的ConcurrentSkipListSet提供了一个并发Set实现。SkipList集只是一种特殊的集合实现。它仍然实现了Serializable, Cloneable, Iterable, Collection, NavigableSet, Set, SortedSet接口。如果您只需要Set接口,这可能对您有用。

Set<String> mySet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
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;
}
}

正如所指出的,获得可并发HashSet的最佳方法是使用Collections.synchronizedSet()

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

这对我来说很管用,我还没看到有人真正指出这一点。

正如Eugene指出的那样,这比目前批准的解决方案效率要低,因为它只是将您的set包装到一个同步装饰器中,而ConcurrentHashMap实际上实现了低级并发,它可以很好地支持您的set。所以,感谢斯捷潘年科夫先生阐明了这一点。

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

使用番石榴 15你也可以简单地使用:

Set s = Sets.newConcurrentHashSet();

就像雷Toal提到的那样,它很简单:

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

为什么不使用java.util.concurrent中的:CopyOnWriteArraySet ?