เหตุใดจึงไม่มี ConcurrentHashSet เทียบกับ ConcurrentHashMap


537

HashSet ขึ้นอยู่กับ HashMap

ถ้าเราดูที่การดำเนินการทุกอย่างจะถูกจัดการภายใต้HashSet<E>HashMap<E,Object>

<E> ถูกใช้เป็นกุญแจของ HashMapถูกนำมาใช้เป็นกุญแจสำคัญของ

และเรารู้ว่าHashMapมันไม่ปลอดภัย นั่นคือเหตุผลที่เรามีConcurrentHashMapใน Java

จากนี้ฉันกำลังสับสนว่าทำไมเราไม่ได้มี ConcurrentHashSet ซึ่งควรจะอยู่บนพื้นฐานที่ConcurrentHashMap?

มีอะไรอีกบ้างที่ฉันขาดหายไป? ฉันต้องการใช้Setในสภาพแวดล้อมแบบมัลติเธรด

นอกจากนี้ถ้าผมต้องการที่จะสร้างของตัวเองConcurrentHashSetผมจะประสบความสำเร็จได้โดยเพียงแค่การเปลี่ยนHashMapไปConcurrentHashMapและออกจากส่วนที่เหลือเป็นคืออะไร?


2
หลังจากมองไปที่ API ถ้าฉันเดาฉันจะบอกว่ามันดูเหมือนว่าจะลดลงถึง 2 ปัจจัย (1) หลีกเลี่ยงการสร้างคลาสใน Java API สำหรับฟังก์ชั่นเล็ก ๆ น้อย ๆ ทุกอย่างที่จำเป็น (2) จัดหาคลาสความสะดวกสบายสำหรับ วัตถุที่ใช้บ่อยขึ้น ฉันชอบ LinkedHashMap และ LinkedHashSet เป็นการส่วนตัวเพราะพวกเขารับประกันคำสั่งซื้อเหมือนกับคำสั่งแทรกเหตุผลเดียวที่ใช้ชุดคือเพื่อหลีกเลี่ยงการทำซ้ำบ่อยครั้งที่ฉันยังต้องการรักษาลำดับการแทรก
อาลี

1
@Ali ฉันเองชอบ LinkedHashMap และ LinkedHashSetคุณจะไปไกล :)
bestsss

9
คำถามเก่า ๆ แต่เป็นผลลัพธ์แรกใน Google อาจเป็นประโยชน์ที่จะทราบว่า ConcurrentSkipListSet มีการใช้งานของ ConcurrentHashMap แล้ว ดูdocs.oracle.com/javase/7/docs/api/java/util/concurrent/ …
อิกอร์โรดริเกซ

1
สิ่งที่ผมเห็นจากแหล่ง Java ConcurrentSkipListSetถูกสร้างขึ้นบนConcurrentSkipListMapซึ่งการดำเนินการและConcurrentNavigableMap ConcurrentMap
Talha Ahmed Khan

คำตอบ:


581

ไม่มีประเภทในตัวConcurrentHashSetเพราะคุณสามารถหาชุดจากแผนที่ได้ตลอดเวลา เนื่องจากมีแผนที่หลายประเภทคุณจึงใช้วิธีสร้างชุดจากแผนที่ที่กำหนด (หรือคลาสของแผนที่)

ก่อนหน้า Java 8 คุณจะสร้างชุดแฮชที่เกิดขึ้นพร้อมกันซึ่งได้รับการสนับสนุนโดยแผนที่แฮชพร้อมกันโดยใช้ Collections.newSetFromMap(map)

ใน Java 8 (แหลมออกโดย @ Matt) คุณจะได้รับกัญชาชุดพร้อมกันมุมมองConcurrentHashMap.newKeySet()ผ่าน มันค่อนข้างง่ายกว่าของเก่าnewSetFromMapที่ทำให้คุณต้องผ่านวัตถุแผนที่ที่ว่างเปล่า แต่มันก็เฉพาะเจาะจงConcurrentHashMapแต่มันเป็นเฉพาะกับ

อย่างไรก็ตามผู้ออกแบบ Java สามารถสร้างชุดอินเตอร์เฟสใหม่ทุกครั้งที่มีการสร้างส่วนต่อประสานแผนที่ใหม่ แต่รูปแบบนั้นจะเป็นไปไม่ได้ที่จะบังคับใช้เมื่อบุคคลที่สามสร้างแผนที่ของตัวเอง มันจะดีกว่าที่จะมีวิธีการคงที่ที่ได้รับชุดใหม่ วิธีการนี้จะใช้งานได้แม้ว่าคุณจะสร้างการใช้งานแผนที่ของคุณเอง


4
ฉันถูกต้องหรือไม่ที่จะบอกว่าถ้าคุณสร้างฉากแบบนี้ConcurrentHashMapคุณจะเสียประโยชน์ที่ได้รับไปConcurrentHashMapหรือไม่?
Pacerier

19
ไม่มีประโยชน์ที่จะสูญเสีย newSetFromMapการดำเนินงานของจะพบว่าเริ่มต้นในบรรทัด 3841 ในdocjar.com/html/api/java/util/Collections.java.html มันเป็นเพียงเสื้อคลุม ....
Ray Toal

4
@ แอนดรูว์ฉันคิดว่าแรงจูงใจเบื้องหลังการใช้ "ConcurrentSet" ไม่ใช่จาก API แต่เป็นการนำไปใช้ - ความปลอดภัยของเธรด แต่ไม่มีการล็อคแบบสากล - อ่านพร้อมกันหลายครั้ง
Ustaman Sangat

5
ConcurrentSkipList มีค่าใช้จ่ายจำนวนมาก (ขนาด) และการค้นหาช้ากว่า
eckes

3
ระวังเมื่อใช้วิธีนี้เนื่องจากวิธีการบางอย่างไม่ได้ดำเนินการอย่างถูกต้อง เพียงทำตามการเชื่อมโยง: สร้างCollections.newSetFromMap SetFromMapเช่นSetFromMap.removeAllวิธีการที่ได้รับมอบหมายไปที่สืบทอดมาจากKeySetView.removeAll ConcurrentHashMap$CollectionView.removeAllวิธีนี้ไม่มีประสิทธิภาพในการกำจัดองค์ประกอบจำนวนมาก จินตนาการremoveAll(Collections.emptySet())สำรวจองค์ประกอบทั้งหมดMapโดยไม่ทำอะไรเลย การมีสิ่งConcurrentHashSetที่ถูกนำไปใช้อย่างถูกต้องจะดีกว่าในกรณีส่วนใหญ่
benez


79

ด้วยGuava 15 คุณสามารถใช้:

Set s = Sets.newConcurrentHashSet();

12
นี่เป็นฝันร้ายเสมอ หากคุณมีชุดหรือแผนที่ที่ไม่ได้ระบุว่าสิ่งที่ปลอดภัยหรือไม่นั้นเป็นสิ่งที่ปลอดภัยสำหรับคุณคุณจะพบกับอันตรายและ desasters ทุกชนิดที่เกิดขึ้นใน maintaince ฉันต้องการชนิดที่บ่งบอกถึงความปลอดภัยของเธรดสำหรับคอลเลกชัน (หรือไม่) เสมอ
Martin Kersten

11
คำอธิบายวิธีการอย่างแท้จริง "สร้างชุดเธรดที่ปลอดภัยได้รับการสนับสนุนโดยแผนที่แฮ"
kichik

16
อย่างที่ฉันบอกไปแล้วว่ามี ConcurrentSet <E> หายไป ConcurrentHashMap มาพร้อมกับอินเตอร์เฟส ConcurrentMap เพื่อระบุสิ่งนี้ นี่เป็นเหตุผลเดียวกันกับที่ฉันเพิ่มอินเตอร์เฟส ConcurrentSet นี้เช่นกัน
Martin Kersten

35

เช่นเดียวกับRay Toal ที่กล่าวถึงมันง่ายเหมือน:

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

1
นี้ดูเหมือนว่าจะต้อง Java 8. ConcurrentHashMapมองไปที่การดำเนินการนี้ก็ดูเหมือนจะเป็นเพียงแค่กระดาษห่อของ
Mygod

20

ดูเหมือนว่า Java ให้การดำเนินงานชุดพร้อมกันกับของConcurrentSkipListSet skiplist ชุดเป็นเพียงชนิดพิเศษของการดำเนินงานชุด มันยังคงใช้อินเตอร์เฟซ Serializable, Cloneable, Iterable, Collection, NavigableSet, Set, SortedSet สิ่งนี้อาจใช้ได้ผลกับคุณหากคุณต้องการตั้งค่าส่วนต่อประสานเท่านั้น


12
โปรดทราบว่าConcurrentSkipListSetองค์ประกอบของควรเป็นComparable
user454322

หากคุณต้องการขยายจากชุดที่เกิดขึ้นพร้อมกันนี่เป็นทางออกเดียวที่จะใช้งานได้
ndm13

ConcurrentSkipListMap เพิ่มการลงโทษประสิทธิภาพที่ไม่จำเป็นของการมีโครงสร้างต้นไม้เป็นฐานข้อมูลแทนที่จะใช้ HashTable แม้ว่าคุณไม่ต้องการฟังก์ชันการเรียงลำดับ / การนำทางก็ตาม
Ajeet Ganga

ไม่ได้ใช้ยกเว้นกรณีที่คุณต้องการConcurrentSkipListSet SortedSetการดำเนินการตามปกติเช่นการเพิ่มหรือลบควรจะ O (1) สำหรับHashSetแต่ O (เข้าสู่ระบบ (n)) SortedSetสำหรับ
benez

16

เป็นแหลมจากนี้วิธีที่ดีที่สุดที่จะได้รับการเห็นพ้อง HashSet สามารถเป็นโดยวิธีการของCollections.synchronizedSet()

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

สิ่งนี้ได้ผลกับฉันและฉันไม่เคยเห็นใครชี้ไปเลย

แก้ไขนี้จะมีประสิทธิภาพน้อยกว่าการแก้ปัญหา aproved ปัจจุบันเป็นยูชี้ให้เห็นเพราะมันเป็นเพียงแค่ตัดชุดของคุณให้เป็นมัณฑนากรตรงกันในขณะที่ConcurrentHashMapจริงการดำเนินการเห็นพ้องในระดับต่ำและสามารถย้อนกลับตั้งค่าของคุณเพียงแค่ปรับ ดังนั้นขอขอบคุณ Mr. Stepanenkov ที่ทำให้ชัดเจน

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


16
synchronizedSetวิธีการเพียงแค่สร้างมัณฑนากรภายใต้Collectionวิธีการห่อที่อาจจะด้ายปลอดภัยโดยการประสานทั้งคอลเลกชัน แต่ConcurrentHashMapจะดำเนินการโดยใช้อัลกอริธึมที่ไม่ปิดกั้นและการซิงโครไนซ์ "ระดับต่ำ" โดยไม่มีการล็อคของคอลเลกชันทั้งหมด ดังนั้นกระดาษห่อจากCollections.synchronized... จึงแย่กว่าในสภาพแวดล้อมแบบมัลติเธรดด้วยเหตุผลด้านประสิทธิภาพ
Eugene Stepanenkov

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

ทำไมไม่ใช้: CopyOnWriteArraySet จาก java.util.concurrent?


6
เนื่องจาก CopyOnWriteArraySet คัดลอกคอลเลกชันทั้งหมดในการกลายพันธุ์สถานะใด ๆ ซึ่งไม่ต้องการเสมอเนื่องจากผลกระทบต่อประสิทธิภาพ มันออกแบบมาเพื่อทำงานเฉพาะในกรณีพิเศษ
boneash
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.