ConcurrentHashMap에 대해 ConcurrentHashSet이없는 이유


537

HashSet은 HashMap을 기반으로합니다.

HashSet<E>구현 을 살펴보면 모든 것이에서 관리됩니다 HashMap<E,Object>.

<E>의 키로 사용됩니다 HashMap.

그리고 우리 HashMap는 그것이 스레드 안전하지 않다는 것을 알고 있습니다. 그래서 우리는 ConcurrentHashMapJava를 사용합니다.

이것을 바탕으로, 왜 우리가 ?를 기반으로 해야하는 ConcurrentHashSet이 없는지 혼란 스럽 습니다 ConcurrentHashMap.

내가 놓친 다른 것이 있습니까? Set다중 스레드 환경에서 사용해야 합니다.

또한, 내가 만들려면 내 자신의 ConcurrentHashSet난 그냥 대체하여 그것을 달성 할 수 HashMapConcurrentHashMap와 같이 나머지를 떠나?


2
API를 살펴본 후, 두 가지 요소가 있다고 생각합니다. (1) 필요한 모든 기능에 대해 Java API에서 클래스를 만들 필요가 없습니다. (2) 편의 클래스 제공 더 자주 사용되는 객체. 개인적으로 LinkedHashMap 및 LinkedHashSet을 선호합니다. 순서가 삽입 순서와 동일하다는 것을 보장하기 때문에 세트를 사용하는 유일한 이유는 중복을 피하기 위해서입니다. 종종 삽입 순서를 유지하려고합니다.
Ali

1
@Ali, 나는 개인적으로의 LinkedHashMap 및 LinkedHashSet의 선호 당신이 :) 멀리 갈 것입니다
bestsss

9
조금 오래된 질문이지만 Google의 첫 번째 결과이므로 ConcurrentSkipListSet에 이미 ConcurrentHashMap이 구현되어 있음을 아는 것이 유용 할 수 있습니다. docs.oracle.com/javase/7/docs/api/java/util/concurrent/…를
Igor Rodriguez

1
내가 자바 소스에서보고하는 ConcurrentSkipListSet에 내장되어 ConcurrentSkipListMap있는 구현, ConcurrentNavigableMapConcurrentMap.
Talha Ahmed Khan

답변:


581

ConcurrentHashSet항상 파생 할 수 있기 때문에 내장 유형이 없습니다.지도에서 세트를 . 여러 유형의지도가 있으므로 메소드를 사용하여 지정된지도 (또는지도 클래스)에서 세트를 생성합니다.

Java 8 이전에는 다음을 사용하여 동시 해시 맵이 지원하는 동시 해시 세트를 생성합니다. Collections.newSetFromMap(map)

@Matt가 지적한 Java 8에서는를 통해 동시 해시 세트 를 얻을 수 있습니다 ConcurrentHashMap.newKeySet(). 이것은 newSetFromMap빈 맵 객체를 전달해야 했던 이전보다 약간 간단 합니다. 그러나 그것은 특정ConcurrentHashMap 입니다.

어쨌든, 자바 디자이너들은 새로운 맵 인터페이스가 생성 될 때마다 새로운 세트 인터페이스를 만들 수 있었지만, 제 3자가 자신의 맵을 만들 때 그 패턴을 적용하는 것은 불가능할 것입니다. 새로운 세트를 도출하는 정적 메소드를 사용하는 것이 좋습니다. 이 방법은 자체지도 구현을 만들 때도 항상 작동합니다.


4
에서이 방법으로 세트를 만들면 ConcurrentHashMap얻을 수있는 이점을 잃어 버릴 수 ConcurrentHashMap있습니까?
Pacerier

19
잃을 혜택이 없습니다. newSetFromMap구현은 docjar.com/html/api/java/util/Collections.java.html의 3841 행에서 시작됩니다 . 그것은 .... 단지 래퍼
레이 톨

4
@Andrew, "ConcurrentSet"사용의 동기는 API가 아니라 구현 (스레드 안전성이지만 범용 잠금이 없음)-다중 동시 읽기와 관련이 있다고 생각합니다 .
Ustaman Sangat

5
ConcurrentSkipList에 많은 (크기) 오버 헤드가 있으며 조회 속도가 느립니다.
eckes 2016 년

3
일부 방법은 올바르게 구현되지 않았으므로이 방법을 사용할 때는주의하십시오. 링크를 따라 Collections.newSetFromMap가면 SetFromMap됩니다. 예를 들어, SetFromMap.removeAll메소드 KeySetView.removeAll는에서 상속 하는을 (를) 위임합니다 ConcurrentHashMap$CollectionView.removeAll. 이 방법은 요소를 대량으로 제거 할 때 매우 비효율적입니다. 아무 것도하지 않고 removeAll(Collections.emptySet())모든 요소를 ​​순회 한다고 상상해보십시오 Map. 갖는 ConcurrentHashSetcorretly 구현되는 것은 대부분의 경우에 더 좋을 것이다.
benez

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

79

함께 구아바 (15) 당신은 또한 간단하게 사용할 수 있습니다 :

Set s = Sets.newConcurrentHashSet();

12
이것은 항상 악몽입니다. 나사산이 안전한지 여부를 표시하지 않는 세트 또는 맵이있는 경우 모든 종류의 위험과 재난이 유지 관리에서 발생합니다. 나는 항상 컬렉션에 대한 스레드 안전성을 나타내는 유형을 원할 것입니다.
Martin Kersten

11
방법 설명은 문자 그대로 "해시 맵에 의해 지원되는 스레드 안전 세트를 만듭니다"
kichik

16
내가 말했듯이 ConcurrentSet <E>가 없습니다. ConcurrentHashMap은이를 나타내는 ConcurrentMap 인터페이스와 함께 제공됩니다. 이것이 항상이 ConcurrentSet 인터페이스를 추가하는 것과 동일한 이유입니다.
Martin Kersten

35

마찬가지로 레이 톨은 그것뿐만 아니라 쉽게 언급 :

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

1
이것은 Java 8을 필요로하는 것 같습니다. 구현을 살펴보면, 또한의 래퍼 인 것 같습니다 ConcurrentHashMap.
Mygod

20

Java가 ConcurrentSkipListSet 과 동시 Set 구현을 제공하는 것 같습니다 . SkipList 세트 세트 구현 단지 특별한 종류이다. 아직 직렬화, Cloneable을,의 Iterable, 컬렉션, 해 NavigableSet, 설정, SortedSet의 인터페이스를 구현합니다. Set 인터페이스 만 필요한 경우이 기능을 사용할 수 있습니다.


12
참고 것을 ConcurrentSkipListSet의 요소가 있어야합니다Comparable
user454322

동시 세트에서 확장 해야하는 경우 이것이 유일한 해결책입니다.
ndm13

ConcurrentSkipListMap은 정렬 / 탐색 기능이 필요하지 않은 경우에도 HashTable을 사용하는 대신 트리를 기본 데이터 구조로 사용함으로써 불필요한 성능 저하를 추가합니다.
Ajeet Ganga

ConcurrentSkipListSet원하는 경우 가 아니면 사용 하지 마십시오 SortedSet. add 또는 remove와 같은 일반적인 작업은 a의 경우 O (1) HashSet이지만 a의 경우 O (log (n)) 이어야합니다 SortedSet.
benez

16

에 의해 지적 동시성-수 HashSet의를 얻을 수있는 최선의 방법을 이용하여입니다Collections.synchronizedSet()

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

이것은 나를 위해 일한 나는 사람이 정말 가리키는 보지 못했어요.

편집 Eugene은 세트를 동기화 된 데코레이터로 랩핑하고 ConcurrentHashMap실제로는 낮은 수준의 동시성을 구현하고 세트를 정상적으로 백업 할 수 있기 때문에 Eugene이 지적한대로 현재 입증 된 솔루션보다 덜 효율적 입니다. 이를 분명히 해준 Stepanenkov 씨에게 감사드립니다.

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


16
synchronizedSet방법은 단지 생성 장식을 따라 Collection스레드 안전 전체 컬렉션에 의해 동기화 될 수있는 포장 방법에 관한 것이다. 그러나 전체 콜렉션을 잠그지 않고 비 차단 알고리즘 과 "저수준"동기화를 ConcurrentHashMap사용하여 구현됩니다 . 따라서 성능상의 이유로 멀티 스레드 환경에서 래퍼는 ...에서 나빠집니다. Collections.synchronized
유진 스테파 넨코 프

12

구아바를 사용 Sets.newSetFromMap(map)하여 구할 수 있습니다 . Java 6에도 그 방법이 있습니다java.util.Collections


java.utll.Collections에서 사용할 수 있으며 CHM 세트는 일반적으로 어쨌든 나쁜 것입니다.
bestsss

예, Java 6에 추가 된 것으로 나타났습니다. 따라서 답변에 추가되었습니다
Bozho

주요한 것은 그것이 ThreadSafe라면, 나는 정말로 그것을 의심합니다.
Talha Ahmed Khan

@Talha, 스레드 안전하지만 스레드 안전만으로는 아무 의미가 없습니다
bestsss

때로는 모든 것을 의미합니다. 일반적으로 동시 매핑의 필요성을 최소화하는 방식으로 구현되는 알고리즘의 일부가 아니면 성능 문제가 심각하지 않습니다.
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
나는 더미 객체 대신 Boolean.TRUE를 사용하는 아이디어를 좋아합니다. 조금 더 우아합니다. 널에 맵핑 된 경우에도 키 세트에서 사용 가능하므로 NULL을 사용할 수도 있습니다.
Martin Kersten

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.