ConcurrentHashMapに対するConcurrentHashSetがない理由


537

HashSetはHashMapに基づいています。

HashSet<E>実装を見ると、すべてがで管理されていHashMap<E,Object>ます。

<E>のキーとして使用されますHashMap

そして、それHashMapはスレッドセーフではないことを知っています。そのためConcurrentHashMap、Java を使用しています。

これに基づいて、なぜ私たちは?に基づくべきConcurrentHashSetを持っていないのか混乱していConcurrentHashMapます。

他に欠けているものはありますか?Setマルチスレッド環境で使用する必要があります。

また、自分で作成したい場合はConcurrentHashSetHashMapto ConcurrentHashMapを置き換えて残りをそのままにすることでそれを実現できますか?


2
APIを見て、推測すると、(1)必要な機能の少しずつJava APIでクラスを作成する必要がなくなる(2)の便利なクラスを提供するより頻繁に使用されるオブジェクト。私は個人的にLinkedHashMapとLinkedHashSetを好みます。これらは順序が挿入順序と同じであることを保証するためです。セットを使用する唯一の理由は重複を避けるためであり、挿入順序を維持したいことがよくあります。
アリ

1
@アリ、私は個人的にはLinkedHashMapとLinkedHashSet優先します:)
bestsss

9
少し古い質問ですが、Googleでの最初の結果なので、ConcurrentSkipListSetには既にConcurrentHashMapの実装があることを知っておくと役立ちます。docs.oracle.com/javase/7/docs/api/java/util/concurrent/…を
Igor Rodriguez

1
私はJavaソースから見たことはConcurrentSkipListSet上に構築されConcurrentSkipListMap、その実装ConcurrentNavigableMapConcurrentMap
Talha Ahmed Khan 2013

回答:


581

ConcurrentHashSet常にマップからセットを派生できるため、組み込みのタイプはありません。マップには多くのタイプがあるため、メソッドを使用して、特定のマップ(またはマップクラス)からセットを作成します。

Java 8より前のバージョンでは、コンカレントハッシュマップに基づくコンカレントハッシュセットを作成するには、 Collections.newSetFromMap(map)

Java 8(@Mattで指摘)では、を使用して同時ハッシュセットビューを取得できますConcurrentHashMap.newKeySet()。これはnewSetFromMap、空のマップオブジェクトを渡す必要があった以前のものよりも少し単純です。しかし、それはに固有ConcurrentHashMapです。

とにかく、Javaデザイナーは、新しいマップインターフェイスが作成されるたびに新しいセットインターフェイスを作成することができますが、サードパーティが独自のマップを作成するときに、そのパターンを適用することは不可能です。新しいセットを派生させる静的メソッドを用意することをお勧めします。独自のマップ実装を作成した場合でも、このアプローチは常に機能します。


4
この方法でセットを作成すると、ConcurrentHashMap得られるメリットが失われると言ってもいいでしょうConcurrentHashMapか。
Pacerier

19
失うメリットはありません。 newSetFromMapの実装はdocjar.com/html/api/java/util/Collections.java.htmlの 3841行目から見つかります。それは単なるラッパー....だ
レイTOAL

4
@Andrew、 "ConcurrentSet"を使用する背後にある動機は、APIではなく、実装-スレッドセーフであるが、ユニバーサルロックなし- たとえば、複数の同時読み取りなどに由来すると思います。
Ustaman Sangat 2012

5
ConcurrentSkipListには多くの(サイズ)オーバーヘッドがあり、ルックアップが遅くなります。
eckes '10 / 06/15

3
一部のメソッドが正しく実装されていないため、このアプローチを使用するときは注意してください。リンクに従ってください:をCollections.newSetFromMap作成しSetFromMapます。たとえば、SetFromMap.removeAllメソッドはKeySetView.removeAll、から継承するにデリゲートしConcurrentHashMap$CollectionView.removeAllます。この方法は、要素の一括削除において非常に非効率的です。何もせずにのremoveAll(Collections.emptySet())すべての要素をトラバースすることを想像してくださいMapConcurrentHashSetそれが正しく実装されていることは、ほとんどの場合より優れています。
benez 2018年


79

グアバ 15あなたは、単に使用することができます。

Set s = Sets.newConcurrentHashSet();

12
これは常に悪夢です。何かがスレッドセーフかどうかを示さないセットまたはマップがある場合、あらゆる種類の危険と障害が維持管理で発生します。コレクションのスレッドセーフを示す(またはそうでない)タイプが常に必要です。
マーティンカーステン、2015

11
メソッドの説明は、文字通り「ハッシュマップに
基づく

16
言ったように、ConcurrentSet <E>がありません。ConcurrentHashMapには、これを示すConcurrentMapインターフェースが付属しています。これは、このConcurrentSetインターフェースを常に追加するのと同じ理由です。
Martin Kersten、

35

同様レイTOALが、それは同じように簡単です言及しました。

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

1
これにはJava 8が必要なようです。実装を見ると、これものラッパーにすぎないようですConcurrentHashMap
Mygod 2018年

20

JavaはConcurrentSkipListSetで並行Set実装を提供しているようです。A SkipListセットセットの実装だけの特別な種類です。それでも、Serializable、Cloneable、Iterable、Collection、NavigableSet、Set、SortedSetインターフェイスを実装します。Setインターフェースだけが必要な場合は、これでうまくいくかもしれません。


12
ConcurrentSkipListSetの要素はComparable
次のとおりである

並行しているセットから拡張する必要がある場合、これが機能する唯一のソリューションです。
ndm13 16

ConcurrentSkipListMapは、並べ替え/ナビゲーション機能が不要な場合でも、HashTableを使用する代わりに、ツリーをベースデータ構造として持つという不必要なパフォーマンスペナルティを追加します。
Ajeet Ganga

必要な場合以外は使用しConcurrentSkipListSetないでくださいSortedSet。追加や削除などの通常の操作は、の場合はO(1)ですが、のHashSet場合はO(log(n))ですSortedSet
benez 2018年

16

これによって指摘されいるように、同時実行可能なHashSetを取得する最良の方法は、Collections.synchronizedSet()

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

これは私にとってはうまくいきました、そして実際にそれを指している人を見たことはありません。

編集これは、現在承認されているソリューションよりも効率的ではありません。これは、Eugeneが指摘するように、セットを同期デコレータにラップするだけですが、ConcurrentHashMap実際には低レベルの同時実行を実装し、セットを同じように正常に戻すことができるためです。それを明確にしてくれたステパネンコフ氏に感謝します。

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


16
synchronizedSetこの方法は、単に作成デコレータを下にCollectionスレッドセーフなコレクション全体の同期によるかもしれないラップ方式へ。ただし、コレクション全体をロックすることなく、非ブロッキングアルゴリズムと「低レベル」同期をConcurrentHashMap使用して実装されます。したがって、パフォーマンス上の理由から、マルチスレッド環境では... からのラッパーのほうが適しています。Collections.synchronized
ユージーンステパネンコフ2015年

12

グアバを使っSets.newSetFromMap(map)て入手できます。Java 6にもそのメソッドがありますjava.util.Collections


それはjava.utll.Collectionsで利用可能であり、CHMのセットは通常、とにかく悪いことです。
-bestsss

はい、私はそれがJava 6で追加されていることに気付いたので、答えに追加しました
Bozho

主な理由は、それがThreadSafeの場合であり、私はそれを本当に疑っています。
Talha Ahmed Khan、2011

@Talha、それのスレッドセーフでは、しかし、安全性だけでは何も手段スレッド
bestsss

時にはそれがすべてを意味します。並行マッピングの必要性が最小限に抑えられる方法で通常実装されるアルゴリズムの一部でない限り、これはパフォーマンスの問題です。
Martin Kersten、2015

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
ダミーオブジェクトの代わりにBoolean.TRUEを使用するのが好きです。それはもう少しエレガントです。また、NULLにマップされている場合でもキーセットで使用できるため、NULLの使用も可能です。
マーティンカーステン、2015

2
@MartinKersten fyi、ConcurrentHashMapはnull値を許可しません
Lauri Lehtinen

2

なぜ使用しない:java.util.concurrentのCopyOnWriteArraySet?


6
なぜなら、CopyOnWriteArraySetは、状態の変化に応じてコレクション全体をコピーするからです。これは、パフォーマンスへの影響のため、常に必要とは限りません。特別な場合にのみ機能するように設計されています。
boneash
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.