ConcurrentHashMap'e karşı neden ConcurrentHashSet yoktur?


537

HashSet, HashMap'i temel alır.

HashSet<E>Uygulamaya bakarsak , her şey altında yönetilir HashMap<E,Object>.

<E>anahtar olarak kullanılır HashMap.

Ve bunun HashMapiş parçacığı için güvenli olmadığını biliyoruz . Bu yüzden ConcurrentHashMapJava'da var.

Buna dayanarak, neden dayanacak bir ConcurrentHashSet'e sahip olmadığımız konusunda kafam karıştı ?ConcurrentHashMap

Kaçırdığım başka bir şey var mı? SetÇok iş parçacıklı bir ortamda kullanmam gerekiyor .

Ayrıca, ben oluşturmak istiyorsanız benim kendi ConcurrentHashSetSadece değiştirerek bunu başarabilir HashMapetmek ConcurrentHashMapve olduğu gibi kalan bırakarak?


2
API'ya baktıktan sonra, tahmin edersem 2 faktöre inmiş gibi göründüğümü söyleyebilirim, (1) gereken her işlevsellik için Java API'de bir sınıf oluşturmaktan kaçınmak (2) daha sık kullanılan nesneler. Ben şahsen LinkedHashMap ve LinkedHashSet tercih sipariş çünkü ekleme sipariş aynıdır, bir küme kullanmanın tek nedeni yineleme önlemek için, genellikle hala ekleme siparişi korumak istiyorum.
Ali

1
@Ali, şahsen LinkedHashMap ve LinkedHashSet'i çok tercih edeceğim :)
bestsss

9
Biraz eski bir soru, ancak Google'daki ilk sonuç olduğu için ConcurrentSkipListSet'in zaten ConcurrentHashMap uygulamasına sahip olduğunu bilmek yararlı olabilir. Bkz. Docs.oracle.com/javase/7/docs/api/java/util/concurrent/…
Igor Rodriguez

1
Java kaynağından gördüklerim ConcurrentSkipListSetüzerine kuruludur ConcurrentSkipListMap, ConcurrentNavigableMapve bunu uygular ConcurrentMap.
Talha Ahmed Khan

Yanıtlar:


581

Her ConcurrentHashSetzaman haritadan bir set türetebileceğiniz için yerleşik bir tür yoktur . Birçok harita türü olduğundan, belirli bir haritadan (veya harita sınıfından) bir küme oluşturmak için bir yöntem kullanırsınız.

Java 8'den önce, kullanarak eşzamanlı karma haritasının desteklediği bir eşzamanlı karma üretirsiniz Collections.newSetFromMap(map)

(@Matt tarafından işaret) Java 8, bir eşzamanlı karma seti alabilirsiniz görünümü aracılığıyla ConcurrentHashMap.newKeySet(). Bu, newSetFromMapboş bir harita nesnesine geçmenizi gerektiren eskisinden biraz daha basittir . Ancak, bu spesifiktir ConcurrentHashMap.

Her neyse, Java tasarımcıları yeni bir harita arayüzü her oluşturulduğunda yeni bir set arayüzü oluşturabilirdi, ancak üçüncü tarafların kendi haritalarını oluşturduklarında bu kalıbı uygulamak imkansız olurdu. Yeni kümeler türeyen statik yöntemlere sahip olmak daha iyidir; kendi harita uygulamalarınızı oluştururken bile bu yaklaşım her zaman işe yarar.


4
Seti bu şekilde oluşturursanız ConcurrentHashMap, elde edeceğiniz avantajları kaybedeceğinizi söyleyebilir miyim ConcurrentHashMap?
Pacerier

19
Kaybedecek bir fayda yok. newSetFromMap'nin uygulaması doc41.com/html/api/java/util/Collections.java.html adresindeki 3841 satırından başlayarak bulunur . Bu sadece bir sarıcı ....
Ray Toal

4
@Andrew, bir "ConcurrentSet" kullanmanın arkasındaki motivasyon API değil, uygulama - iş parçacığı güvenliği ama evrensel bir kilit olmadan - örneğin birden fazla eşzamanlı okuma kaynaklanıyor düşünüyorum.
Ustaman Sangat

5
ConcurrentSkipList'in çok sayıda (boyut) ek yükü vardır ve aramalar daha yavaştır.
Eckes

3
bazı yaklaşımlar doğru uygulanmadığından bu yaklaşımı kullanırken dikkatli olun. Sadece bağlantıları izleyin: Collections.newSetFromMapoluşturur a SetFromMap. örneğin, SetFromMap.removeAllyöntem devralınan KeySetView.removeAll, devralınan yöntem ConcurrentHashMap$CollectionView.removeAll. Bu yöntem, dökme çıkarma elemanlarında oldukça verimsizdir. Hayal et removeAll(Collections.emptySet()), Maphiçbir şey yapmadan tüm unsurları geçer . Bir Having ConcurrentHashSetcorretly uygulandığını çoğu durumda daha iyi olacaktır.
benez

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

79

İle Guava 15 de basitçe kullanabilirsiniz:

Set s = Sets.newConcurrentHashSet();

12
Bu her zaman bir kabustur. Bir şeyin iş parçacığı açısından güvenli olup olmadığını göstermeyen bir kümeniz veya haritanız varsa, her türlü tehlikenin ve afetin bakımda olduğunu görürsünüz. Her zaman koleksiyonlar için iş parçacığı güvenliğini (ya da değil) gösteren bir tür istiyorum.
Martin Kersten

11
Yöntem açıklaması tam anlamıyla "Bir karma harita tarafından desteklenen bir iş parçacığı güvenli kümesi oluşturur"
kichik

16
Dediğim gibi, bir ConcurrentSet <E> eksik. ConcurrentHashMap bunu belirtmek için bir ConcurrentMap arayüzü ile birlikte gelir. Bu aynı zamanda bu ConcurrentSet arabirimini de eklememle aynı nedendir.
Martin Kersten

35

Gibi Ray Toal o kadar kolaydır söz:

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

1
Bu Java 8 gerektiriyor gibi görünüyor. Uygulamaya bakıldığında, bu da sadece bir sarıcı gibi görünüyor ConcurrentHashMap.
Mygod

20

Görünüşe göre Java, ConcurrentSkipListSet ile eşzamanlı bir Set uygulaması sağlıyor . Bir SkipList Seti kümesi uygulama sadece özel bir türüdür. Serializable, Cloneable, Iterable, Collection, NavigableSet, Set, SortedSet arabirimlerini uygular. Yalnızca Set arayüzüne ihtiyacınız varsa bu işinize yarayabilir.


12
Unutmayın ki ConcurrentSkipListSetelementlerComparable
user454322

Eşzamanlı bir Kümeden genişletmeniz gerekirse, burada çalışacak tek çözüm budur.
ndm13

ConcurrentSkipListMap, sıralama / gezinme işlevine gerek duymasanız bile HashTable kullanmak yerine ağacın temel veri yapısı olarak kullanılmasına gereksiz performans cezası ekler.
Ajeet Ganga

ConcurrentSkipListSetistemediğiniz sürece kullanmayın SortedSet. Toplama veya kaldırma gibi olağan bir işlem a için O (1), a için HashSetO (log (n)) olmalıdır SortedSet.
benez

16

Tarafından belirttiği gibi bu bir eşzamanlılık-muktedir HashSet elde etmenin en iyi yolu vasıtasıyla olduğunuCollections.synchronizedSet()

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

Bu benim için işe yaradı ve kimsenin gerçekten ona işaret ettiğini görmedim.

EDIT Bu, şu anda onaylanmış çözümden daha az verimlidir, çünkü Eugene'in belirttiği gibi, setinizi senkronize bir dekoratöre sararken, ConcurrentHashMapaslında düşük seviyeli bir eşzamanlılık uygular ve Setinizi de aynı derecede iyi destekleyebilir. Bay Stepenkenkov'a bunu açıkladığı için teşekkürler.

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


16
synchronizedSetyöntem sadece yaratır dekoratör altında Collectionevreli Bütün koleksiyon senkronizasyonu ile olabilir sarma yöntemleri. Ancak ConcurrentHashMap, tüm koleksiyonun herhangi bir kilidi olmadan bloke olmayan algoritmalar ve "düşük seviye" senkronizasyonları kullanılarak uygulanır. Yani Collections.synchronized... ' dan sarıcılar performans nedeniyle çok iş parçacıklı ortamlarda daha kötüdür.
Eugene Stepanenkov

12

Bir Sets.newSetFromMap(map)tane almak için guava'ları kullanabilirsiniz . Java 6'da şu yöntem de vardır:java.util.Collections


java.utll.Collections ve CHM kümesi genellikle kötü bir şeydir zaten.
bestsss

evet, Java 6'da eklendiğini fark ettim, bu yüzden cevaba ekledim
Bozho

Ana konu ThreadSafe ise ve gerçekten bundan şüphe duyuyorum.
Talha Ahmed Khan

@Talha, iş parçacığı güvenli, ancak iş parçacığı güvenliği tek başına bir şey ifade etmiyor
bestsss 9:11

Bazen her şey demektir. Genellikle eşzamanlı haritalama ihtiyacını en aza indirecek şekilde uygulanan bir algoritmanın parçası olmadığı sürece bir performans problemi.
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
Sahte bir nesne yerine Boolean.TRUE kullanma fikrini seviyorum. Biraz daha zarif. Ayrıca nULL ile eşlenmiş olsa bile anahtar kümesinde kullanılabileceğinden NULL kullanmak da mümkündür.
Martin Kersten

2
@MartinKersten fyi, ConcurrentHashMap null değerlere izin vermiyor
Lauri Lehtinen

2

Neden kullanmıyorsunuz: java.util.concurrent'dan CopyOnWriteArraySet?


6
Çünkü CopyOnWriteArraySet, tüm koleksiyonun, performans etkisi nedeniyle her zaman istenmeyen herhangi bir durum mutasyonuna kopyalar. Sadece özel durumlarda çalışmak üzere tasarlanmıştır.
boneash
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.