Почему нет ConcurrentHashSet против ConcurrentHashMap - PullRequest
476 голосов
/ 09 августа 2011

HashSet основан на HashMap.

Если мы посмотрим на реализацию HashSet<E>, все управляется в HashMap<E,Object>.

<E> используется в качестве ключа HashMap.

И мы знаем, что HashMap не является потокобезопасным.Вот почему у нас ConcurrentHashMap в Java.

Исходя из этого, я запутался в том, что , почему у нас нет ConcurrentHashSet, который должен основываться на ConcurrentHashMap?

Есть ли что-то еще, что мне не хватает?Мне нужно использовать Set в многопоточной среде.

Кроме того, если я хочу создать свой собственный ConcurrentHashSet, могу ли я добиться этого, просто заменив HashMap на ConcurrentHashMap и оставивотдых как есть?

Ответы [ 9 ]

530 голосов
/ 09 августа 2011

Нет встроенного типа для ConcurrentHashSet, потому что вы всегда можете получить набор из карты. Поскольку существует много типов карт, вы используете метод для создания набора из данной карты (или класса карты).

До Java 8 вы создаете параллельный хеш-набор, поддерживаемый одновременной хэш-картой, используя Collections.newSetFromMap(map)

В Java 8 (на что указывает @Matt) вы можете получить одновременный хэш-набор view через ConcurrentHashMap.newKeySet(). Это немного проще, чем старый newSetFromMap, который требовал от вас передать пустой объект карты. Но это специфично для ConcurrentHashMap.

В любом случае, разработчики Java могли создавать новый интерфейс набора каждый раз, когда создавался новый интерфейс карты, но этот шаблон было бы невозможно применить, когда третьи стороны создают свои собственные карты. Лучше иметь статические методы, которые получают новые наборы; этот подход всегда работает, даже когда вы создаете свои собственные реализации карт.

93 голосов
/ 23 июня 2014
Set<String> mySet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
70 голосов
/ 01 февраля 2015

С Гуавой 15 вы также можете просто использовать:

Set s = Sets.newConcurrentHashSet();
26 голосов
/ 07 июля 2016

Как Рэй Тоал упомянул, что это так же просто, как:

Set<String> myConcurrentSet = ConcurrentHashMap.newKeySet();
18 голосов
/ 02 октября 2013

Похоже, что Java предоставляет параллельную реализацию Set с ConcurrentSkipListSet . SkipList Set - это просто особая реализация набора. Он по-прежнему реализует интерфейсы Serializable, Cloneable, Iterable, Collection, NavigableSet, Set, SortedSet. Это может работать для вас, если вам нужен только интерфейс Set.

14 голосов
/ 05 ноября 2014

Как указано , этот лучший способ получить HashSet с поддержкой параллелизма с помощью Collections.synchronizedSet()

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

Это сработало для меня, и я не видел, чтобы кто-нибудь действительно указывал на это.

EDIT Это менее эффективно, чем одобренное в настоящее время решение, как указывает Юджин, поскольку оно просто оборачивает ваш набор в синхронизированный декоратор, тогда как ConcurrentHashMap фактически реализует низкоуровневый параллелизм и может поддержите свой набор так же хорошо. Так что спасибо г-ну Степаненкову за то, что он это прояснил.

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

12 голосов
/ 09 августа 2011

Вы можете использовать гуаву Sets.newSetFromMap(map), чтобы получить ее.Java 6 также имеет этот метод в java.util.Collections

5 голосов
/ 25 сентября 2014
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;
   }
}
2 голосов
/ 07 октября 2016

Почему бы не использовать: CopyOnWriteArraySet из java.util.concurrent?

...