จะเกิดอะไรขึ้นเมื่อใส่รหัสซ้ำลงใน HashMap


276

ถ้าผมผ่านคีย์เดียวกันหลายครั้งเพื่อให้HashMap's putวิธีการสิ่งที่เกิดขึ้นไปเป็นค่าเดิมหรือไม่? และถ้าเกิดว่าแม้แต่ค่าซ้ำ ฉันไม่พบเอกสารใด ๆ เกี่ยวกับเรื่องนี้

กรณีที่ 1: เขียนทับค่าสำหรับคีย์

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
System.out.println(mymap.get("1"));

surely not oneเราได้รับ

กรณีที่ 2: ค่าซ้ำ

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
// The following line was added:
mymap.put("1","one");
System.out.println(mymap.get("1"));

oneเราได้รับ

แต่จะเกิดอะไรขึ้นกับค่าอื่น ๆ ฉันสอนพื้นฐานให้กับนักเรียนและฉันถูกถามเรื่องนี้ ที่Mapเก็บข้อมูลเหมือนกับที่อ้างอิงค่าสุดท้าย (แต่อยู่ในหน่วยความจำ) หรือไม่?


7
BTW นี่เป็นโอกาสที่ยอดเยี่ยมในการแสดง multi-hashmap ที่เป็นส่วนหนึ่งของคลาสคอลเลกชันจาการ์ตา ( commons.apache.org/collections ) มันจะช่วยให้คุณมีจำนวนค่าใด ๆ ที่เกี่ยวข้องกับคีย์เดียวกันสำหรับเวลาเหล่านั้นเมื่อคุณต้องการ
John Munsch

คำตอบ:


303

ตามคำนิยามputคำสั่งจะแทนที่ค่าก่อนหน้าที่เชื่อมโยงกับคีย์ที่กำหนดในแผนที่ (แนวคิดในการดำเนินการทำดัชนีอาร์เรย์สำหรับประเภทดั้งเดิม)

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

ข้อมูลเพิ่มเติมที่นี่: HashMap Doc


ขอบคุณสำหรับสิ่งนี้. การอ่านแม้ว่าเอกสาร Java จะไม่ได้กล่าวถึงอย่างชัดเจน ฉันคาดเดาว่าผู้เขียนเอกสารสันนิษฐานว่านี่เป็นข้อสมมติโดยนัยของการใช้งานแผนที่แฮชทั้งหมด
Andrew S

77

คุณอาจพบคำตอบของคุณใน javadoc ของMap # put (K, V) (ซึ่งคืนค่าบางอย่าง):

public V put(K key,
             V value)

เชื่อมโยงค่าที่ระบุกับคีย์ที่ระบุในแผนที่นี้ (การดำเนินการเพิ่มเติม) หากแผนที่ก่อนหน้านี้มีการแมปสำหรับคีย์นี้ค่าเก่าจะถูกแทนที่ด้วยค่าที่ระบุ (แผนที่mถูกกล่าวเพื่อให้มีการแมปสำหรับคีย์kถ้าหากm.containsKey(k)จะส่งคืนtrueเท่านั้น)

พารามิเตอร์:
key - คีย์ซึ่งค่าที่ระบุจะเชื่อมโยง
value- ค่าที่จะเชื่อมโยงกับคีย์ที่ระบุ

ผลตอบแทน:
ค่าก่อนหน้านี้ที่เกี่ยวข้องกับคีย์ที่ระบุหรือถ้ามีการทำแผนที่ไม่null key(การnullส่งคืนยังสามารถระบุว่าแผนที่ก่อนหน้านี้เชื่อมโยงnullกับที่ระบุkeyหากการใช้งานรองรับnullค่า)

ดังนั้นหากคุณไม่ได้กำหนดค่าที่ส่งคืนเมื่อโทรmymap.put("1", "a string")มันจะไม่ได้รับการยืนยันและมีสิทธิ์สำหรับการรวบรวมขยะ


3
ค่ากลับมาเป็นค่าก่อนหน้า (หรือnull) เป็นเอกสารเหนือใน Javadoc เพื่อใช่นี่คือสิ่งที่ผมหมายถึง ตีความผิดได้จริงหรือ?
Pascal Thivent

สิ่งนี้มีประโยชน์มาก
roottraveller

18

ค่าก่อนหน้าสำหรับคีย์จะถูกดร็อปและแทนที่ด้วยค่าใหม่

หากคุณต้องการเก็บค่าทั้งหมดที่ได้รับคีย์คุณอาจลองใช้สิ่งนี้:

import org.apache.commons.collections.MultiHashMap;
import java.util.Set;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
public class MultiMapExample {

   public static void main(String[] args) {
      MultiHashMap mp=new MultiHashMap();
      mp.put("a", 10);
      mp.put("a", 11);
      mp.put("a", 12);
      mp.put("b", 13);
      mp.put("c", 14);
      mp.put("e", 15);
      List list = null;

      Set set = mp.entrySet();
      Iterator i = set.iterator();
      while(i.hasNext()) {
         Map.Entry me = (Map.Entry)i.next();
         list=(List)mp.get(me.getKey());

         for(int j=0;j<list.size();j++)
         {
          System.out.println(me.getKey()+": value :"+list.get(j));
         }
      }
   }
}

1
วิธีการแก้ปัญหานี้จะถูก depricated MultiHashMap เป็นส่วนหนึ่งของ apache.commons.collections ไม่ใช่ java
wikimix

17

มันเป็นคุณสมบัติคีย์ / ค่าและคุณไม่สามารถมีคีย์ซ้ำสำหรับหลายค่าได้เนื่องจากเมื่อคุณต้องการรับค่าจริงซึ่งเป็นหนึ่งในค่าที่เป็นของคีย์ที่ป้อน
ในตัวอย่างของคุณเมื่อคุณต้องการรับค่าของ "1" มัน!
นั่นเป็นเหตุผลที่มีคีย์เฉพาะสำหรับทุกค่า แต่คุณสามารถมีเคล็ดลับโดย java มาตรฐาน lib:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class DuplicateMap<K, V> {

    private Map<K, ArrayList<V>> m = new HashMap<>();

    public void put(K k, V v) {
        if (m.containsKey(k)) {
            m.get(k).add(v);
        } else {
            ArrayList<V> arr = new ArrayList<>();
            arr.add(v);
            m.put(k, arr);
        }
    }

     public ArrayList<V> get(K k) {
        return m.get(k);
    }

    public V get(K k, int index) {
        return m.get(k).size()-1 < index ? null : m.get(k).get(index);
    }
}


และคุณสามารถใช้วิธีนี้ได้:

    public static void main(String[] args) {
    DuplicateMap<String,String> dm=new DuplicateMap<>();
    dm.put("1", "one");
    dm.put("1", "not one");
    dm.put("1", "surely not one");
    System.out.println(dm.get("1"));
    System.out.println(dm.get("1",1));
    System.out.println(dm.get("1", 5));
}

และผลลัพธ์ของการพิมพ์คือ:

[one, not one, surely not one]
not one
null

คำตอบที่ดี! งานที่ดี คุณช่วยชีวิตการเขียนโปรแกรมของฉันอย่างแท้จริง :)
Subin Babu

ขอบคุณจากฉันเช่นกัน! ฉันต้องเพิ่มวิธี "ลบ" เพื่อทำหน้าที่เหมือนกับแผนที่ปกติ แต่ใช้งานได้ดีมาก!
JGlass

1
@JGlass เพื่อนต้อนรับของคุณ แต่นี่ไม่ใช่วิธีแก้ปัญหาทางเทคนิคนั่นคือสิ่งที่คุณสามารถทำได้โดย java มาตรฐาน lib ในปัญหาทางเทคนิคคุณต้องระวังปัญหาของคุณถ้าคุณจำเป็นต้องมีพฤติกรรมนี้ฉันแน่ใจว่ามันไม่ใช่วิธีแก้ปัญหาเพราะ ของแนวคิด Key / Value และต้องคิดเกี่ยวกับปัญหาและหาวิธีแก้ปัญหาแบบตรรกะ อย่างไรก็ตามรายละเอียดของฉันเป็นเพียงวิธีที่สนุกที่จะทำกับ java และในการผลิตปัญหาและเส้นทางของการแก้ไขแตกต่างกันมากในการทำงานสนุก! แต่คุณสามารถใช้มันได้เมื่อพฤติกรรมของคีย์ / ค่าไม่ใช่ปัญหาของคุณและการค้นหาให้มีโครงสร้างข้อมูลแบบนั้น
java acm

13

เชื่อมโยงค่าที่ระบุกับคีย์ที่ระบุในแผนที่นี้ หากแผนที่ก่อนหน้านี้มีการทำแผนที่สำหรับคีย์ค่าเก่าจะถูกแทนที่


12

มันจะแทนที่ค่าที่มีอยู่ในแผนที่สำหรับคีย์ที่เกี่ยวข้อง และหากไม่มีคีย์ที่ใช้ชื่อเดียวกันมันจะสร้างคีย์พร้อมค่าที่ระบุ เช่น:

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","two");

คีย์OUTPUT = "1", value = "two"

ดังนั้นค่าก่อนหน้านี้จึงถูกเขียนทับ


4

สำหรับคำถามของคุณไม่ว่าแผนที่จะเป็นที่เก็บข้อมูลหรือไม่: ไม่

มันเหมือนกับรายการที่มีname=valueคู่โดยที่nameไม่จำเป็นต้องเป็น String (สามารถทำได้)

ในการรับองค์ประกอบคุณจะต้องส่งกุญแจของคุณไปยังวิธี get () - ซึ่งให้วัตถุที่กำหนดกลับมาให้คุณ

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

นี่จะไม่มีประสิทธิภาพ มันจะคำนวณแฮชโค้ดที่เรียกว่าจากวัตถุทั้งสองและเปรียบเทียบสิ่งเหล่านั้น มันง่ายกว่าที่จะเปรียบเทียบสองints แทนที่จะเป็นวัตถุทั้งสอง คุณสามารถจินตนาการรหัสแฮชโค้ดเช่นข้อมูลสรุปที่มีความยาวที่กำหนดไว้ล่วงหน้า (int) ดังนั้นจึงไม่ซ้ำกันและมีการชนกัน คุณพบกฎสำหรับ hashcode ในเอกสารที่ฉันได้ใส่ลิงค์

หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้คุณอาจต้องการดูบทความเกี่ยวกับjavapractices.comและtechnofundo.com

ความนับถือ


3

ฉันมักจะใช้:

HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();

หากฉันต้องการใช้หลายสิ่งกับรหัสที่ระบุหนึ่งอัน

public void MultiHash(){
    HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();
    String key = "Your key";

    ArrayList<String> yourarraylist = hashy.get(key);

    for(String valuessaved2key : yourarraylist){
        System.out.println(valuessaved2key);
    }

}

คุณสามารถทำอะไรเช่นนี้และสร้างเขาวงกตให้ตัวเองอยู่เสมอ!

public void LOOK_AT_ALL_THESE_HASHMAPS(){
    HashMap<String, HashMap<String, HashMap<String, HashMap<String, String>>>> theultimatehashmap = new HashMap <String, HashMap<String, HashMap<String, HashMap<String, String>>>>();
    String ballsdeep_into_the_hashmap = theultimatehashmap.get("firststring").get("secondstring").get("thirdstring").get("forthstring");
}

2

แผนที่จาก JDK ไม่ได้มีไว้สำหรับการจัดเก็บข้อมูลภายใต้คีย์ซ้ำ

  • ค่าใหม่ที่ดีที่สุดจะแทนที่ค่าก่อนหน้า

  • สถานการณ์แย่ลงเป็นข้อยกเว้น (เช่นเมื่อคุณพยายามรวบรวมเป็นสตรีม):

ไม่มีรายการซ้ำ:

Stream.of("one").collect(Collectors.toMap(x -> x, x -> x))

ตกลง. คุณจะได้รับ: $ 2 ==> {หนึ่ง = หนึ่ง}

สตรีมที่ซ้ำกัน:

Stream.of("one", "not one", "surely not one").collect(Collectors.toMap(x -> 1, x -> x))

ข้อยกเว้น java.lang.IllegalStateException: คีย์ซ้ำ 1 (พยายามผสานค่าหนึ่งค่าและไม่ใช่ค่าหนึ่ง) | ที่ Collector.duplicateKeyException (Collector.java:133) | ที่ Collector.lambda $ uniqKeysMapAccumulator $ 1 (Collector.java:180) | ที่ ReduceOps $ 3ReducingSink.accept (ReduceOps.java:169) | ที่ Spliterators $ ArraySpliterator.forEachRemaining (Spliterators.java:948) | ที่ AbstractPipeline.copyInto (AbstractPipeline.java:484) | ที่ AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:474) | ที่ ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:913) ที่ AbstractPipeline.evaluate (AbstractPipeline.java:234) | ที่ ReferencePipeline.collect (ReferencePipeline.java,578) | ที่ (# 4: 1)

ในการจัดการกับคีย์ที่ซ้ำกัน - ใช้แพ็คเกจอื่นเช่น: https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html

มีการใช้งานอื่น ๆ อีกมากมายที่เกี่ยวข้องกับคีย์ที่ทำซ้ำ สิ่งเหล่านี้จำเป็นสำหรับเว็บ (เช่นคีย์คุกกี้ที่ซ้ำกันส่วนหัว Http สามารถมีฟิลด์เดียวกันได้ ... )

โชคดี! :)


การดำเนินการ "แทนที่" มีราคาแพงหรือไม่
gaurav

สามารถแก้ไขได้โดยใช้ JDK เท่านั้น Collectors.toMap()มีฟังก์ชั่นที่สาม - ผสาน Stream.of("one", "two", "one").collect(Collectors.toMap(x -> x, x -> x, (key1, key2) -> key2))ถ้าเราต้องการที่จะเพียงแค่แทนที่องค์ประกอบที่ซ้ำกันครั้งสุดท้ายเมื่อ: ลิงก์
ยืนอยู่คนเดียว

ด้วยตัวอย่างรหัสที่สองของคุณไม่ถูกต้อง อินพุตนี้: "one", "not one", "surely not one"จะไม่สร้างข้อผิดพลาดที่สำคัญใด ๆ เนื่องจากสตริงทั้งหมดแตกต่างกัน
ยืนอยู่คนเดียว

สวัสดี @ ยืนอยู่คนเดียว โปรดอ่านอย่างละเอียดฟังก์ชั่นการทำแผนที่ (toMap)
Witold Kaczurba

สวัสดี @WitoldKaczurba กรุณารวบรวมรหัสของคุณก่อนโพสต์
ยืนอยู่คนเดียว

1

BTW หากคุณต้องการซีแมนทิกส์บางอย่างเช่นใส่เฉพาะในกรณีที่ไม่มีคีย์นี้ คุณสามารถใช้concurrentHashMapกับputIfAbsent()ฟังก์ชั่น ลองดู:

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html#put(K,%20V)

concurrentHashMap เป็นเธรดที่ปลอดภัยและมีประสิทธิภาพสูงเนื่องจากใช้กลไก" lock striping " เพื่อปรับปรุงปริมาณงาน


1

ใช่นี่หมายความว่า 1 คีย์ทั้งหมดที่มีค่าถูกเขียนทับด้วยมูลค่าเพิ่มล่าสุดและที่นี่คุณเพิ่ม "ไม่แน่นอนอย่างใดอย่างหนึ่ง" ดังนั้นมันจะแสดงเฉพาะ "แน่นอนไม่ได้หนึ่ง"

แม้ว่าคุณกำลังพยายามแสดงด้วยการวนซ้ำมันก็จะแสดงเพียงหนึ่งคีย์และค่าที่มีคีย์เดียวกัน


0
         HashMap<Emp, Emp> empHashMap = new HashMap<Emp, Emp>();

         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp());
         empHashMap.put(new Emp(1), new Emp());
         System.out.println(empHashMap.size());
    }
}

class Emp{
    public Emp(){   
    }
    public Emp(int id){
        this.id = id;
    }
    public int id;
    @Override
    public boolean equals(Object obj) {
        return this.id == ((Emp)obj).id;
    }

    @Override
    public int hashCode() {
        return id;
    }
}


OUTPUT : is 1

หมายถึงแผนที่แฮชจะไม่อนุญาตให้ซ้ำกันหากคุณมีวิธีการแทนที่อย่างถูกต้องและวิธีการ hashCode ()

HashSet ยังใช้ HashMap ภายในดูแหล่งเอกสาร

public class HashSet{
public HashSet() {
        map = new HashMap<>();
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.