Por que não há ConcurrentHashSet contra ConcurrentHashMap


537

O HashSet é baseado no HashMap.

Se observarmos a HashSet<E>implementação, tudo será gerenciado HashMap<E,Object>.

<E>é usado como uma chave de HashMap.

E sabemos que isso HashMapnão é seguro para threads. É por isso que temos ConcurrentHashMapem Java.

Com base nisso, estou confuso de que por que não temos um ConcurrentHashSet que deve ser baseado no ConcurrentHashMap?

Falta mais alguma coisa? Eu preciso usar Setem um ambiente multiencadeado.

Além disso, se eu quiser criar o meu próprio, ConcurrentHashSetposso consegui-lo apenas substituindo o HashMappara ConcurrentHashMape deixando o resto como está?


2
Depois de analisar a API, se eu adivinhar, diria que ela parece se resumir a 2 fatores, (1) evitar a necessidade de criar uma classe na API Java para todas as funcionalidades necessárias (2) fornecer classes de conveniência para objetos usados ​​com mais frequência. Pessoalmente, prefiro o LinkedHashMap e o LinkedHashSet, pois eles garantem que o pedido é o mesmo que o pedido de inserção. O único motivo para usar um conjunto é evitar a duplicação. Muitas vezes, ainda quero manter o pedido de inserção.
Ali

1
@Ali, eu pessoalmente prefiro LinkedHashMap e LinkedHashSet você vai longe :)
bestsss

9
Uma pergunta um pouco antiga, mas como é o primeiro resultado no Google, pode ser útil saber que o ConcurrentSkipListSet já possui a implementação do ConcurrentHashMap. Veja docs.oracle.com/javase/7/docs/api/java/util/concurrent/…
Igor Rodriguez

1
O que eu vi da fonte Java ConcurrentSkipListSeté construído ConcurrentSkipListMap, o que implementa ConcurrentNavigableMape ConcurrentMap.
precisa

Respostas:


581

Não existe um tipo incorporado ConcurrentHashSetporque você sempre pode derivar um conjunto de um mapa. Como existem muitos tipos de mapas, você usa um método para produzir um conjunto a partir de um determinado mapa (ou classe de mapa).

Antes do Java 8, você produz um conjunto de hash simultâneo apoiado por um mapa de hash simultâneo, usando Collections.newSetFromMap(map)

No Java 8 (apontado por @Matt), você pode obter uma visualização simultânea de conjunto de hash via ConcurrentHashMap.newKeySet(). Isso é um pouco mais simples que o antigo, newSetFromMapque exigia que você passasse um objeto de mapa vazio. Mas é específico para ConcurrentHashMap.

De qualquer forma, os designers de Java poderiam ter criado uma nova interface de conjunto toda vez que uma nova interface de mapa fosse criada, mas esse padrão seria impossível de aplicar quando terceiros criam seus próprios mapas. É melhor ter os métodos estáticos que derivam novos conjuntos; essa abordagem sempre funciona, mesmo quando você cria suas próprias implementações de mapas.


4
Estou certo em dizer que, se você criar o cenário dessa maneira ConcurrentHashMap, perderá os benefícios dos quais obteria ConcurrentHashMap?
Pacerier

19
Não há benefícios a perder. newSetFromMapA implementação de é encontrada a partir da linha 3841 em docjar.com/html/api/java/util/Collections.java.html . É apenas um wrapper ....
Ray Toal

4
@ Andrew, acho que a motivação por trás do uso de um "ConcurrentSet" decorre não da API, mas da implementação - segurança de thread, mas sem um bloqueio universal - várias leituras simultâneas, por exemplo.
Ustaman Sangat 13/09/12

5
ConcurrentSkipList tem muita sobrecarga (tamanho) e as pesquisas são mais lentas.
Eckes

3
tenha cuidado ao usar essa abordagem, pois alguns métodos não foram implementados corretamente. Basta seguir os links: Collections.newSetFromMapcria um SetFromMap. por exemplo, o SetFromMap.removeAllmétodo delega para o KeySetView.removeAll, que herda de ConcurrentHashMap$CollectionView.removeAll. Este método é altamente ineficiente para remover elementos em massa. imagine removeAll(Collections.emptySet())atravessar todos os elementos no Mapsem fazer nada. Ter um ConcurrentHashSetque seja implementado corretamente será melhor na maioria dos casos.
Benez

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

79

Com o Guava 15 você também pode simplesmente usar:

Set s = Sets.newConcurrentHashSet();

12
Isso é sempre um pesadelo. Se você possui um conjunto ou um mapa que não indica se algo é ou não seguro para threads, você encontra todos os tipos de perigos e desastres que ocorrem em manutenção. Eu sempre desejaria um tipo que indique segurança de threads para coleções (ou não).
Martin Kersten

11
A descrição do método é literalmente "Cria um conjunto thread-safe apoiada por um mapa de hash"
Kichik

16
Como eu disse, há um ConcurrentSet <E> ausente. ConcurrentHashMap vem junto com uma interface ConcurrentMap para indicar isso. Esse é o mesmo motivo pelo qual sempre adiciono essa interface ConcurrentSet também.
Martin Kersten

35

Como Ray Toal mencionou, é tão fácil quanto:

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

1
Isso parece exigir o Java 8. Observando a implementação, isso também parece ser apenas um invólucro ConcurrentHashMap.
Mygod

20

Parece que o Java fornece uma implementação simultânea do conjunto com seu ConcurrentSkipListSet . Um conjunto SkipList é apenas um tipo especial de implementação de conjunto. Ele ainda implementa as interfaces Serializable, Cloneable, Iterable, Collection, NavigableSet, Set, SortedSet. Isso pode funcionar para você se você precisar apenas da interface Set.


12
Observe que ConcurrentSkipListSetos elementos devem serComparable
user454322

Se você precisar estender a partir de um conjunto simultâneo, esta é a única solução aqui que funcionará.
Ndm13

ConcurrentSkipListMap adiciona uma penalidade de desempenho desnecessária por ter a árvore como estrutura de dados base, em vez de usar o HashTable, mesmo quando você não precisa da funcionalidade de classificação / navegação.
Ajeet ganga

não use a ConcurrentSkipListSetmenos que você queira SortedSet. Uma operação usual como adicionar ou remover deve ser O (1) para a HashSet, mas O (log (n)) para a SortedSet.
219 benez

16

Como apontado por isso a melhor maneira de obter um HashSet simultaneidade capaz é por meio deCollections.synchronizedSet()

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

Isso funcionou para mim e eu não vi ninguém realmente apontando para isso.

EDIT Isso é menos eficiente do que a solução atualmente aprovada, como Eugene aponta, uma vez que apenas envolve seu conjunto em um decorador sincronizado, enquanto um ConcurrentHashMapimplementa a simultaneidade de baixo nível e pode fazer o seu conjunto da mesma maneira. Então, obrigado ao Sr. Stepanenkov por deixar isso claro.

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


16
o synchronizedSetmétodo apenas cria o decorador em baixo Collectionpara agrupar métodos que poderiam ser seguros para threads pela sincronização de toda a coleção. Mas ConcurrentHashMapé implementado usando algoritmos sem bloqueio e sincronizações de "baixo nível" sem bloqueios de toda a coleção. Portanto, os invólucros de Collections.synchronized... são piores em ambientes com vários threads por motivos de desempenho.
Eugene Stepanenkov 01/01

12

Você pode usar goiabas Sets.newSetFromMap(map)para obter uma. O Java 6 também possui esse método emjava.util.Collections


está disponível em java.utll.Collections e conjunto de CHM geralmente é uma coisa ruim de qualquer maneira.
bestsss 9/08/11

sim, eu notei que é adicionado em Java 6, então acrescentou que para a resposta
Bozho

O principal é que, se é ThreadSafe, e eu realmente duvido disso.
Talha Ahmed Khan

@Talha, é o segmento de seguros, no entanto fio de segurança por si só não significa nada
bestsss

Às vezes significa tudo. Isso representa um problema de desempenho, a menos que faça parte de um algoritmo que geralmente é implementado de maneira a minimizar a necessidade de mapeamento simultâneo.
Martin Kersten

5
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
Eu gosto da ideia de usar Boolean.TRUE em vez de um objeto fictício. É um pouco mais elegante. Também é possível usar NULL, pois ele estaria disponível no conjunto de chaves, mesmo que mapeado para nulo.
Martin Kersten

2
@MartinKersten fyi, ConcurrentHashMap não permite valores nulos #
Lauri Lehtinen

2

Por que não usar: CopyOnWriteArraySet de java.util.concurrent?


6
Porque CopyOnWriteArraySet copia a coleção inteira em qualquer mutação de estado, que nem sempre é desejada devido ao impacto no desempenho. Ele foi projetado para funcionar apenas em casos especiais.
boneash
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.