ConcurrentHashMap에 대해 ConcurrentHashSet이없는 이유


537

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

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

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

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

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

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

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


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있는 구현, ConcurrentNavigableMap및 ConcurrentMap.
— 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.