ตรวจสอบการมีอยู่ของคีย์ใน HashMap


309

การตรวจสอบการมีอยู่ของคีย์ใน HashMap จำเป็นหรือไม่

ฉันมี HashMap โดยพูด 1,000 รายการและฉันกำลังมองหาการปรับปรุงประสิทธิภาพ หากมีการเข้าถึง HashMap บ่อยมากการตรวจสอบการมีอยู่ของคีย์ในการเข้าถึงทุกครั้งจะนำไปสู่ค่าใช้จ่ายจำนวนมาก หากไม่มีคีย์และด้วยเหตุนี้จึงมีข้อยกเว้นเกิดขึ้นฉันสามารถตรวจจับข้อยกเว้นได้ (เมื่อฉันรู้ว่าสิ่งนี้จะเกิดขึ้นน้อยมาก) สิ่งนี้จะลดการเข้าถึง HashMap ลงครึ่งหนึ่ง

นี่อาจไม่ใช่วิธีการเขียนโปรแกรมที่ดี แต่มันจะช่วยลดจำนวนการเข้าถึง หรือฉันกำลังพลาดบางสิ่งที่นี่

[ อัพเดท ] ฉันไม่มีค่า Null ใน HashMap


8
"ด้วยเหตุนี้และข้อยกเว้นเกิดขึ้น" - ข้อยกเว้นอะไร? สิ่งนี้จะไม่ได้มาจาก java.util.HashMap ...
serg10

คำตอบ:


513

คุณเคยเก็บค่า Null หรือไม่? ถ้าไม่คุณสามารถทำได้:

Foo value = map.get(key);
if (value != null) {
    ...
} else {
    // No such key
}

มิฉะนั้นคุณสามารถตรวจสอบการมีอยู่หากคุณได้รับค่า Null คืน

Foo value = map.get(key);
if (value != null) {
    ...
} else {
    // Key might be present...
    if (map.containsKey(key)) {
       // Okay, there's a key but the value is null
    } else {
       // Definitely no such key
    }
}

1
@ Samuel: เฉพาะเมื่อมีค่า null เป็นไปได้ หากคุณไม่มีค่าว่างในแผนที่แน่นอนgetว่าใช้ได้และหลีกเลี่ยงการค้นหาสองครั้งเมื่อคุณต้องการค่าเช่นกัน
Jon Skeet

แม้ว่านี่อาจเป็นตัวอย่างที่ชัดเจนกว่า แต่คุณสามารถเขียนif(value!=null || map.containsKey(key))ส่วนที่สองได้ อย่างน้อยถ้าคุณต้องการทำสิ่งเดียวกันทั้งสองวิธี - ไม่มีรหัสซ้ำ มันจะทำงานเนื่องจากไฟฟ้าลัดวงจร
Cullub

66

คุณจะไม่ได้อะไรจากการตรวจสอบว่ามีกุญแจอยู่หรือไม่ นี่คือรหัสของHashMap:

@Override
public boolean containsKey(Object key) {
    Entry<K, V> m = getEntry(key);
    return m != null;
}

@Override
public V get(Object key) {
    Entry<K, V> m = getEntry(key);
    if (m != null) {
        return m.value;
    }
    return null;
}

เพียงแค่ตรวจสอบว่าค่าตอบแทนสำหรับการจะแตกต่างจากget()null

นี่คือซอร์สโค้ด HashMap


ทรัพยากร:


2
มีจุดประสงค์อะไรในการแสดงการติดตั้งใช้งานที่เฉพาะเจาะจงของวิธีการเหล่านี้
jarnbjo

2
เพื่ออธิบายว่าในกรณีส่วนใหญ่การตรวจสอบว่าคีย์มีอยู่จะใช้เวลาประมาณเดียวกับการรับค่า ดังนั้นมันจะไม่ปรับอะไรให้เหมาะสมเพื่อตรวจสอบว่ามีคีย์อยู่จริงก่อนรับค่า ฉันรู้ว่ามันเป็นลักษณะทั่วไป แต่สามารถช่วยให้เข้าใจได้
Colin Hebert

ลิงค์ที่ดีคือgrepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/ (OpenJDK นั้นได้มาจากรหัส Sun อย่างมาก) และดูเหมือนว่าฉันผิด ฉันเปรียบเทียบรุ่นสำหรับ Java5 กับ Java6; มันทำงานแตกต่างกันในพื้นที่นี้ (แต่ทั้งคู่ถูกต้องเช่นเดียวกับตัวอย่างที่คุณโพสต์)
Donal Fellows

2
ตามที่ระบุไว้ในคำตอบที่ยอมรับแล้วนักต้มตุ๋นคนนี้ก็ผิดธรรมดา แน่นอนว่าคุณจะได้รับบางสิ่งบางอย่างโดยการตรวจสอบค่าที่อยู่ของผู้เปรียบเทียบคีย์ - คุณสามารถแยกความแตกต่างของคีย์ที่ไม่มีอยู่จากคีย์ที่มีอยู่ แต่แมปกับโมฆะเป็นค่า
Johannes H.

43

วิธีที่ดีกว่าคือการใช้วิธีการของcontainsKey HashMapพรุ่งนี้ใครบางคนจะเพิ่ม null ลงในแผนที่ คุณควรแยกความแตกต่างระหว่างการมีอยู่ของคีย์และคีย์มีค่า Null


ใช่. หรือซับคลาส HashMap เพื่อป้องกันการจัดเก็บnullทั้งหมด
RobAu

1
1+ สำหรับประเภทดึกดำบรรพ์เนื่องจากไม่จำเป็นต้องใช้มูลค่าของการร่ายโดยไม่ต้องใช้คำตอบนี้
Prashant Bhanarkar

นอกจากนี้ยังมีความคล่องแคล่วในการเขียน. กัปตันKK () มากกว่าการตรวจสอบหาค่าว่าง เราควรกังวลมากขึ้นเกี่ยวกับการอ่านง่ายซึ่งช่วยประหยัดเวลาของนักพัฒนามากกว่าการเพิ่มประสิทธิภาพเล็กน้อยในกรณีส่วนใหญ่ อย่างน้อยก็ไม่ปรับให้เหมาะสมก่อนที่จะจำเป็น
สูงสุด

23

คุณหมายถึงว่าคุณได้รับรหัสเช่น

if(map.containsKey(key)) doSomethingWith(map.get(key))

ทั่วทุกสถานที่ ? จากนั้นคุณควรตรวจสอบว่ามีการmap.get(key)คืนค่าว่างหรือไม่ โดยวิธีการที่ HashMap ไม่โยนข้อยกเว้นสำหรับกุญแจที่หายไปมันจะคืนค่าว่างแทน กรณีเดียวที่containsKeyจำเป็นคือเมื่อคุณจัดเก็บค่า Null เพื่อแยกความแตกต่างระหว่างค่า Null และค่าที่ขาดหายไป แต่โดยทั่วไปถือว่าเป็นวิธีปฏิบัติที่ไม่ดี


8

เพียงใช้containsKey()เพื่อความชัดเจน มันรวดเร็วและทำให้โค้ดสะอาดและสามารถอ่านได้ จุดทั้งหมดของHashMapการค้นหาคีย์นั้นรวดเร็วเพียงตรวจสอบให้แน่ใจhashCode()และequals()ใช้งานอย่างถูกต้อง



3

นอกจากนี้คุณยังสามารถใช้computeIfAbsent()วิธีการในHashMapชั้นเรียน

ในตัวอย่างต่อไปนี้mapเก็บรายการธุรกรรม (จำนวนเต็ม) ที่ใช้กับคีย์ (ชื่อของบัญชีธนาคาร) หากต้องการเพิ่ม 2 การทำธุรกรรม100และ200การchecking_accountที่คุณสามารถเขียน:

HashMap<String, ArrayList<Integer>> map = new HashMap<>();
map.computeIfAbsent("checking_account", key -> new ArrayList<>())
   .add(100)
   .add(200);

วิธีนี้คุณไม่ต้องตรวจสอบเพื่อดูว่ามีกุญแจchecking_accountอยู่หรือไม่

  • หากไม่มีอยู่จะมีการสร้างและส่งคืนโดยนิพจน์แลมบ์ดา
  • computeIfAbsent()ถ้ามันมีอยู่แล้วค่าสำหรับคีย์จะถูกส่งกลับโดย

สง่างามจริงๆ! 👍


0

ฉันมักจะใช้สำนวน

Object value = map.get(key);
if (value == null) {
    value = createValue(key);
    map.put(key, value);
}

ซึ่งหมายความว่าคุณจะกดแผนที่สองครั้งเท่านั้นหากไม่มีคีย์


0
  1. หากคลาสหลักเป็นของคุณตรวจสอบให้แน่ใจว่าวิธีการ hashCode () และ equals () ถูกนำไปใช้
  2. โดยทั่วไปการเข้าถึง HashMap ควรเป็น O (1) แต่ด้วยการใช้วิธี hashCode ที่ไม่ถูกต้องมันจะกลายเป็น O (n) เนื่องจากค่าที่มีคีย์แฮชเดียวกันจะถูกเก็บไว้เป็นรายการที่เชื่อมโยง

0

คำตอบ Jon Skeet ตอบโจทย์ทั้งสองสถานการณ์ได้ดี (แผนที่ที่มีnullคุณค่าและไม่ใช่nullคุณค่า) อย่างมีประสิทธิภาพ

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

ฉันมี HashMap ด้วยคำพูด 1.000 รายการและฉันกำลังมองหาการปรับปรุงประสิทธิภาพ หากมีการเข้าถึง HashMap บ่อยมากการตรวจสอบการมีอยู่ของคีย์ในการเข้าถึงทุกครั้งจะนำไปสู่ค่าใช้จ่ายจำนวนมาก

แผนที่ที่มีรายการ 1,000 รายการไม่ใช่แผนที่ขนาดใหญ่
เช่นเดียวกับแผนที่ที่มีรายการ 5.000 หรือ 10.000
Mapถูกออกแบบมาเพื่อทำการดึงข้อมูลอย่างรวดเร็วด้วยมิติดังกล่าว

ทีนี้ก็ถือว่าhashCode()ปุ่มแผนที่ให้การกระจายที่ดี

หากคุณสามารถใช้Integerเป็นประเภทคีย์ได้
ใช้hashCode()วิธีการที่มีประสิทธิภาพมากตั้งแต่การชนกันเป็นไปไม่ได้สำหรับที่ไม่ซ้ำกันintค่า:

public final class Integer extends Number implements Comparable<Integer> {
    ...
    @Override
    public int hashCode() {
        return Integer.hashCode(value);
    }

    public static int hashCode(int value) {
        return value;
    }
    ...
}

ถ้าสำหรับกุญแจคุณต้องใช้บิวด์อินอื่นเป็นStringตัวอย่างที่มักใช้Mapคุณอาจมีการชนกัน แต่จาก 1 ถึง 1,000 ถึงบางส่วนของออบเจ็กต์ในMapคุณควรมีน้อยมากเป็นString.hashCode()วิธี ให้การกระจายที่ดี

หากคุณใช้ประเภทที่กำหนดเองให้แทนที่hashCode()และequals()ถูกต้องและให้แน่ใจว่าภาพรวมนั้นhashCode()มีการแจกแจงที่ยุติธรรม
คุณอาจอ้างถึงรายการที่ 9 ของการJava Effectiveอ้างอิง
นี่คือโพสต์ที่มีรายละเอียดวิธีการ

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