การเพิ่มค่าที่ซ้ำกันให้กับ HashSet / HashMap จะแทนที่ค่าก่อนหน้าหรือไม่


141

โปรดพิจารณาโค้ดด้านล่าง:

HashSet hs = new HashSet();
hs.add("hi"); -- (1)
hs.add("hi"); -- (2)

hs.size()จะให้ 1 as HashSetไม่อนุญาตให้ทำซ้ำดังนั้นจะจัดเก็บองค์ประกอบเดียวเท่านั้น

ฉันต้องการทราบว่าถ้าเราเพิ่มองค์ประกอบที่ซ้ำกันแล้วมันแทนที่องค์ประกอบก่อนหน้าหรือไม่เพิ่มเข้าไป?

นอกจากนี้จะเกิดอะไรขึ้นHashMapในกรณีเดียวกัน?

คำตอบ:


251

ในกรณีนี้HashMapจะแทนที่ค่าเก่าด้วยค่าใหม่

ในกรณีนี้HashSetจะไม่มีการแทรกรายการ


1
ไม่แน่ใจว่าฉันขาดอะไรไป แต่ดูเหมือนว่าซอร์สโค้ดจะบ่งบอกเป็นอย่างอื่น? ผมเห็นว่าพวกเขาไม่ทำตรวจสอบในการสนับสนุนHashMapเพื่อดูว่าkeyมีอยู่แล้วก่อนที่จะเรียกputในการสนับสนุนmap?
mystarrocks

10
@mystarrocks: กุญแจสำคัญคือองค์ประกอบของSetและสิ่งนั้นจะไม่ถูกแทนที่ด้วยการput()ดำเนินการ
Keppil

1
ฉันเข้าใจแล้ว ฉันเข้าใจว่าคีย์คือองค์ประกอบของSetแต่เพิ่งรู้ว่าput()จะแทนที่ค่าเท่านั้นไม่ใช่คีย์ ในกรณีนี้จะเป็นค่าเดียวกันที่ใส่ไว้ข้างคีย์อีกครั้งซึ่งอาจจะดีกว่าการตรวจสอบว่ามีคีย์อยู่และใส่หรือไม่ ไม่ว่าจะด้วยวิธีใดฉันเข้าใจวิธีการทำงาน
mystarrocks

1
แค่อยากรู้ว่าทำไม HashMap และ HashSet ถึงเลือกที่จะเป็นเช่นนั้น?
Helin Wang

@HelinWang: ฉันไม่คิดว่ามันถูกวางแผนไว้ฉันคิดว่ามันเป็นเพียงผลกระทบจากHashSetการใช้งานในรูปแบบของไฟล์HashMap. ยากที่จะรู้เว้นแต่คุณจะเป็นหนึ่งในผู้พัฒนาชั้นเรียน
Keppil

47

สิ่งแรกที่คุณต้องรู้คือHashSetทำหน้าที่เหมือน a Setซึ่งหมายความว่าคุณเพิ่มวัตถุของคุณลงในโดยตรงHashSetและไม่สามารถมีรายการที่ซ้ำกันได้ HashSetคุณเพียงแค่เพิ่มมูลค่าของคุณโดยตรงใน

อย่างไรก็ตามHashMapเป็นMapประเภท นั่นหมายความว่าทุกครั้งที่คุณเพิ่มรายการคุณจะต้องเพิ่มคู่คีย์ - ค่า

ในHashMapคุณสามารถมีค่าที่ซ้ำกันได้ แต่ต้องไม่ทำซ้ำคีย์ ในHashMapรายการใหม่จะแทนที่รายการเก่า รายการล่าสุดจะอยู่ในไฟล์HashMap.

การทำความเข้าใจการเชื่อมโยงระหว่าง HashMap และ HashSet:

จำไว้ว่าHashMapไม่สามารถมีคีย์ที่ซ้ำกันได้ เบื้องหลังHashSetใช้ไฟล์HashMap.

เมื่อคุณพยายามที่จะเพิ่มวัตถุใด ๆ ลงในHashSetรายการนี้จะถูกจัดเก็บจริงเป็นกุญแจสำคัญในการHashMap- เดียวกันที่ใช้อยู่เบื้องหลังฉากHashMap HashSetเนื่องจากพื้นฐานนี้HashMapต้องการคู่คีย์ - ค่าเราจึงสร้างค่าดัมมี่ให้เรา

ตอนนี้เมื่อคุณพยายามแทรกวัตถุที่ซ้ำกันอื่นเข้าไปในวัตถุเดียวกันHashSetอีกครั้งจะพยายามแทรกเป็นกุญแจในการHashMapโกหกที่อยู่ด้านล่าง อย่างไรก็ตามHashMapไม่สนับสนุนรายการที่ซ้ำกัน ดังนั้นHashSetจะยังคงส่งผลให้มีค่าประเภทนั้นเพียงค่าเดียว หมายเหตุด้านข้างสำหรับทุกคีย์ที่ซ้ำกันเนื่องจากค่าที่สร้างขึ้นสำหรับรายการของเราใน HashSet เป็นค่าสุ่ม / ดัมมี่คีย์จะไม่ถูกแทนที่เลย มันจะถูกละเว้นเมื่อลบคีย์และเพิ่มคีย์เดียวกันกลับเข้าไป (ค่าดัมมี่เหมือนกัน) จะไม่สมเหตุสมผลเลย

สรุป:

HashMapอนุญาตให้ทำซ้ำvaluesแต่ไม่keysอนุญาต HashSetไม่สามารถมีรายการที่ซ้ำกัน

หากต้องการเล่นว่าการเพิ่มวัตถุสำเร็จหรือไม่คุณสามารถตรวจสอบbooleanค่าที่ส่งคืนเมื่อคุณเรียกใช้.add() และดูว่าส่งคืนtrueหรือfalseไม่ ถ้ามันกลับมาแสดงtrueว่ามันถูกแทรก


HashMap allows duplicate valuesHashMap จะแทนที่ค่าเก่าด้วยค่าใหม่
Alex78191

21

เอกสารจะสวยชัดเจนเกี่ยวกับเรื่องนี้: HashSet.add ไม่ได้แทนที่:

เพิ่มองค์ประกอบที่ระบุให้กับชุดนี้หากยังไม่มีอยู่ อย่างเป็นทางการเพิ่มองค์ประกอบที่ระบุ e ให้กับชุดนี้หากชุดนี้ไม่มีองค์ประกอบ e2 เช่นนั้น (e == null? e2 == null: e.equals (e2)) หากชุดนี้มีองค์ประกอบอยู่แล้วการเรียกจะทำให้ชุดไม่เปลี่ยนแปลงและคืนค่าเป็นเท็จ

แต่จะแทนที่:HashMap.put

หากแผนที่ก่อนหน้านี้มีการแมปสำหรับคีย์ค่าเก่าจะถูกแทนที่


4

เป็นกรณีของ HashSet แต่ไม่สามารถแทนที่ได้

จากเอกสาร:

http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html#add(E )

"เพิ่มองค์ประกอบที่ระบุลงในชุดนี้หากยังไม่ได้แสดงยิ่งขึ้นอย่างเป็นทางการให้เพิ่มองค์ประกอบที่ระบุ e ลงในชุดนี้หากชุดนี้ไม่มีองค์ประกอบ e2 เช่นนั้น (e == null? e2 == null: e.equals ( e2)) หากชุดนี้มีองค์ประกอบอยู่แล้วการเรียกจะทำให้ชุดนั้นไม่เปลี่ยนแปลงและส่งคืนค่าเท็จ "


1

แก้ไขฉันถ้าฉันผิด แต่สิ่งที่คุณได้รับคือการใช้สตริง "สวัสดี" == "สวัสดี" ไม่ได้เป็นจริงเสมอไป (เพราะไม่จำเป็นต้องเป็นวัตถุเดียวกัน)

เหตุผลที่คุณได้รับคำตอบเป็น 1 ก็เพราะ JVM จะนำวัตถุสตริงกลับมาใช้ใหม่หากเป็นไปได้ ในกรณีนี้ JVM กำลังใช้อ็อบเจ็กต์สตริงซ้ำและเขียนทับรายการใน Hashmap / Hashset

แต่คุณไม่รับประกันพฤติกรรมนี้ (เนื่องจากอาจเป็นออบเจ็กต์สตริงอื่นที่มีค่าเดียวกัน "Hi") พฤติกรรมที่คุณเห็นเป็นเพียงเพราะการเพิ่มประสิทธิภาพของ JVM


1

HashMapโดยทั่วไปจะประกอบด้วยEntryซึ่งต่อมามีKey(Object)และValue(Object)ภายในHashSetคือHashMapและHashMapแทนที่ค่าตามที่คุณบางคนชี้ไว้แล้ว.. แต่มันแทนที่คีย์ได้จริงหรือไม่? และนั่นคือเคล็ดลับที่นี่ HashMapเก็บค่าไว้เป็นคีย์ในการอ้างอิงHashMapและค่าเป็นเพียงวัตถุจำลองดังนั้นหากคุณพยายามที่จะใส่ค่าเดียวกันใน HashMap อีกครั้ง (คีย์ในแผนที่ต้นแบบ) มันจะแทนที่ค่าดัมมี่ไม่ใช่คีย์ (ค่าสำหรับแฮชเซ็ต)

ดูรหัสด้านล่างสำหรับ HashSet Class:

public boolean  [More ...] add(E e) {

   return map.put(e, PRESENT)==null;
}

นี่คือค่าของ HashSet แต่คีย์สำหรับแผนที่ที่อยู่เบื้องหลังและจะไม่มีการแทนที่คีย์ หวังว่าฉันจะสามารถเคลียร์ความสับสนได้


0

คุณต้องตรวจสอบวิธีการใส่ในแผนที่ Hash ก่อนเนื่องจาก HashSet ได้รับการสำรองข้อมูลโดย HashMap

  1. เมื่อคุณเพิ่มค่าที่ซ้ำกันให้พูดว่า String "One" ใน HashSet
  2. รายการ ("one", PRESENT) จะถูกแทรกลงใน Hashmap (สำหรับค่าทั้งหมดที่เพิ่มเข้าไปในชุดค่าจะเป็น "PRESENT" ซึ่งหากเป็นประเภท Object)
  3. Hashmap จะเพิ่มรายการลงใน Map และส่งกลับค่าซึ่งในกรณีนี้คือ "PRESENT" หรือ null ถ้าไม่มี Entry
  4. วิธีการเพิ่มของ Hashset จะคืนค่าจริงหากค่าที่ส่งคืนจาก Hashmap เท่ากับ null มิฉะนั้นเป็นเท็จซึ่งหมายความว่ามีรายการอยู่แล้ว ...

0

หากต้องการพูดให้แตกต่างกัน: เมื่อคุณแทรกคู่คีย์ - ค่าลงใน HashMap ซึ่งมีคีย์อยู่แล้ว (ในแง่หนึ่ง hashvalue () ให้ค่าที่ไม่เท่ากัน () เป็นจริง แต่วัตถุทั้งสองยังคงแตกต่างกันได้หลายวิธี ) คีย์จะไม่ถูกแทนที่ แต่ค่าจะถูกเขียนทับ คีย์นี้ใช้เพื่อรับ hashvalue () และค้นหาค่าในตารางด้วย เนื่องจาก HashSet ใช้คีย์ของ HashMap และกำหนดค่าตามอำเภอใจซึ่งไม่สำคัญ (สำหรับผู้ใช้) ด้วยเหตุนี้องค์ประกอบของชุดจึงไม่ถูกแทนที่ด้วย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.