Apa yang terjadi ketika kunci duplikat dimasukkan ke dalam HashMap?

276

Jika saya lulus kunci yang sama beberapa kali untuk HashMap's putmetode, apa yang terjadi pada nilai asli? Dan bagaimana jika bahkan nilainya berulang? Saya tidak menemukan dokumentasi tentang ini.

Kasus 1: Nilai yang ditimpa untuk kunci

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"));

Kami mendapatkan surely not one.

Kasus 2: Nilai rangkap

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"));

Kami mendapatkan one.

Tetapi apa yang terjadi pada nilai-nilai lainnya? Saya mengajarkan dasar-dasar kepada seorang siswa dan saya ditanyai hal ini. Apakah Mapember seperti tempat nilai terakhir direferensikan (tetapi dalam memori)?

boodieye
sumber
7
BTW, ini adalah kesempatan bagus untuk memamerkan multi-hashmap yang merupakan bagian dari kelas koleksi Jakarta ( commons.apache.org/collections ). Ini akan membiarkan Anda memiliki sejumlah nilai yang terkait dengan kunci yang sama untuk saat-saat ketika Anda membutuhkannya.
John Munsch
kemungkinan duplikat HashMap dengan beberapa nilai di bawah kunci yang sama
Muhammed Refaat

Jawaban:

303

Menurut definisi, putperintah ini menggantikan nilai sebelumnya yang terkait dengan kunci yang diberikan di peta (secara konseptual seperti operasi pengindeksan array untuk tipe primitif).

Peta hanya menjatuhkan referensi ke nilai. Jika tidak ada yang memegang referensi ke objek, objek itu menjadi memenuhi syarat untuk pengumpulan sampah. Selain itu, Java mengembalikan nilai sebelumnya yang terkait dengan kunci yang diberikan (atau nulljika tidak ada), sehingga Anda dapat menentukan apa yang ada di sana dan menyimpan referensi jika perlu.

Informasi lebih lanjut di sini: HashMap Doc

jheddings
sumber
Terima kasih untuk ini. Membaca melalui dokumentasi Java ini tidak disebutkan dengan jelas. Saya menduga penulis dokumen menganggap ini sebagai asumsi diam-diam dari semua implementasi peta hash.
Andrew S
77

Anda dapat menemukan jawaban Anda di javadoc Peta # put (K, V) (yang sebenarnya mengembalikan sesuatu):

public V put(K key,
             V value)

Mengaitkan nilai yang ditentukan dengan kunci yang ditentukan dalam peta ini (operasi opsional). Jika peta sebelumnya berisi pemetaan untuk kunci ini, nilai lama diganti dengan nilai yang ditentukan. (Peta mdikatakan berisi pemetaan untuk kunci kjika dan hanya jika m.containsKey(k)akan kembali true.)

Parameter:
key - kunci yang terkait dengan nilai yang ditentukan.
value- nilai yang akan dikaitkan dengan kunci yang ditentukan.

Pengembalian:
nilai sebelumnya yang terkait dengan kunci yang ditentukan, atau nulljika tidak ada pemetaan untuk key. (Pengembalian nulljuga dapat menunjukkan bahwa peta sebelumnya terkait nulldengan yang ditentukan key, jika implementasi mendukung nullnilai-nilai.)

Jadi jika Anda tidak menetapkan nilai yang dikembalikan saat menelepon mymap.put("1", "a string"), itu hanya menjadi tidak direferensikan dan dengan demikian memenuhi syarat untuk pengumpulan sampah.

Thivent Pascal
sumber
3
Nilai yang dikembalikan adalah nilai sebelumnya (atau null) seperti yang didokumentasikan tepat di atas di javadoc jadi, ya, inilah yang saya maksud. Bisakah itu benar-benar disalahartikan?
Pascal Thivent
ini sangat membantu.
roottraveller
18

Nilai sebelumnya untuk kunci dijatuhkan dan diganti dengan yang baru.

Jika Anda ingin menyimpan semua nilai yang diberikan kunci, Anda dapat mempertimbangkan untuk mengimplementasikan sesuatu seperti ini:

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));
         }
      }
   }
}
kamlesh0606
sumber
1
Solusi ini terdeplikasi. MultiHashMap adalah bagian dari apache.commons.collections dan bukan java.
wikimix
17

itu fitur Kunci / Nilai dan Anda tidak dapat memiliki kunci duplikat untuk beberapa nilai karena ketika Anda ingin mendapatkan nilai aktual yang salah satu dari nilai-nilai itu dimasukkan ke
dalam kunci yang dimasukkan dalam contoh Anda saat Anda ingin mendapatkan nilai "1" yang mana Itu ?!
itulah alasan untuk memiliki kunci unik untuk setiap nilai tetapi Anda dapat memiliki trik dengan lib standar java:

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);
    }
}


dan Anda bisa menggunakannya dengan cara ini:

    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));
}

dan hasil cetak adalah:

[one, not one, surely not one]
not one
null
java acm
sumber
Jawaban bagus! kerja bagus. Anda benar-benar menyelamatkan kehidupan pemrograman saya :).
Subin Babu
Terima kasih dari saya juga! Saya memang harus menambahkan metode "hapus" untuk melakukan fungsi yang sama seperti Peta normal tetapi bekerja dengan baik!
JGlass
1
@ JGlass selamat datang Bung Anda, tapi ini bukan solusi teknis, itulah yang dapat Anda lakukan dengan lib standar java, dalam masalah teknis Anda harus memperhatikan masalah Anda, jika Anda perlu memiliki perilaku ini saya yakin itu bukan solusi karena Konsep Kunci / Nilai, dan harus memikirkan masalah dan menemukan cara logis untuk menyelesaikannya. bagaimanapun detail saya hanyalah cara yang menyenangkan untuk dilakukan dengan java dan dalam produksi, masalah dan jalur penyelesaian sangat berbeda dengan pekerjaan yang menyenangkan! tetapi Anda bisa menggunakannya ketika perilaku Key / Value bukan masalah Anda dan menemukan memiliki struktur data seperti itu.
java acm
13

Mengaitkan nilai yang ditentukan dengan kunci yang ditentukan di peta ini. Jika peta sebelumnya berisi pemetaan untuk kunci, nilai lama diganti.

Diego Alejandro
sumber
12

Ini menggantikan nilai yang ada di peta untuk kunci masing-masing. Dan jika tidak ada kunci dengan nama yang sama maka itu menciptakan kunci dengan nilai yang diberikan. misalnya:

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

Tombol OUTPUT = "1", nilai = "dua"

Jadi, nilai sebelumnya akan ditimpa.

Bishal Jaiswal
sumber
4

Untuk pertanyaan Anda apakah peta itu seperti ember: tidak.

Ini seperti daftar dengan name=valuepasangan sedangkan nametidak perlu menjadi String (bisa, meskipun).

Untuk mendapatkan elemen, Anda meneruskan kunci Anda ke get () - metode yang memberi Anda objek yang ditugaskan sebagai imbalan.

Dan peta Hash berarti bahwa jika Anda mencoba untuk mengambil objek Anda menggunakan metode get, itu tidak akan membandingkan objek nyata dengan yang Anda berikan, karena itu perlu mengulangi melalui daftar dan membandingkan () kunci Anda memberikan elemen saat ini.

Ini tidak efisien. Alih-alih, apa pun objek Anda, ia menghitung kode hash yang disebut dari kedua objek dan membandingkannya. Lebih mudah membandingkan dua objek intdaripada dua objek keseluruhan (mungkin sangat kompleks). Anda dapat membayangkan kode hash seperti ringkasan yang memiliki panjang (int) yang telah ditentukan, karena itu tidak unik dan memiliki tabrakan. Anda menemukan aturan untuk kode hash dalam dokumentasi yang saya masukkan tautannya.

Jika Anda ingin tahu lebih banyak tentang ini, Anda mungkin ingin melihat artikel di javapractices.com dan technofundo.com

salam

Atmokasi
sumber
3

Saya selalu menggunakan:

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

jika saya ingin menerapkan banyak hal pada satu kunci pengidentifikasi.

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);
    }

}

Anda selalu bisa melakukan sesuatu seperti ini dan membuat diri Anda labirin!

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");
}
lambiiHD
sumber
2

Peta dari JDK tidak dimaksudkan untuk menyimpan data di bawah kunci duplikat.

  • Paling-paling nilai baru akan menimpa yang sebelumnya.

  • Skenario yang lebih buruk adalah pengecualian (misalnya ketika Anda mencoba mengumpulkannya sebagai aliran):

Tidak ada duplikat:

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

Baik. Anda akan mendapatkan: $ 2 ==> {one = one}

Aliran duplikat:

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

Exception java.lang.IllegalStateException: Duplicate key 1 (mencoba menggabungkan nilai satu dan bukan satu) | di Collectors.duplicateKeyException (Collectors.java:133) | di Collectors.lambda $ uniqKeysMapAccumulator $ 1 (Collectors.java:180) | di ReduceOps $ 3ReducingSink.accept (ReduceOps.java:169) | di Spliterators $ ArraySpliterator.forEachRemaining (Spliterators.java:948) | di AbstractPipeline.copyInto (AbstractPipeline.java:484) | di AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:474) | di ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:913) | di AbstractPipeline.evaluate (AbstractPipeline.java:34) | di ReferencePipeline.collect (ReferencePipeline.java#78) | at (# 4: 1)

Untuk menangani kunci duplikat - gunakan paket lain, misalnya: https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html

Ada banyak implementasi lain yang berhubungan dengan kunci duplikat. Itu diperlukan untuk web (mis. Kunci cookie duplikat, header Http dapat memiliki bidang yang sama, ...)

Semoga berhasil! :)

Witold Kaczurba
sumber
Apakah operasi "override" mahal?
gaurav
Itu bisa diselesaikan menggunakan JDK saja. Collectors.toMap()memiliki argumen ketiga - fungsi gabungan. Jika kita ingin hanya menimpa terakhir duplikat elemen: Stream.of("one", "two", "one").collect(Collectors.toMap(x -> x, x -> x, (key1, key2) -> key2)). link
stand alone
Contoh kode kedua Anda juga tidak benar. Input ini: "one", "not one", "surely not one"tidak akan menghasilkan kesalahan kunci duplikat karena semua string berbeda.
berdiri sendiri
Hai, berdiri sendiri. Harap baca dengan cermat fungsi pemetaan (toMap).
Witold Kaczurba
Hai @WoldoldKaczurba. Silakan kompilasi kode Anda sebelum memposting.
berdiri sendiri
1

Ya, ini berarti semua kunci 1 dengan nilai ditimpa dengan nilai tambah terakhir dan di sini Anda menambahkan "pasti bukan satu" sehingga hanya akan menampilkan "pasti bukan satu".

Sekalipun Anda mencoba menampilkan dengan satu lingkaran, itu juga hanya akan menampilkan satu kunci dan nilai yang memiliki kunci yang sama.

ajaynakrani
sumber
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

Berarti peta hash tidak akan memungkinkan duplikat, jika Anda telah benar ditimpa sama dengan metode hashCode ().

HashSet juga menggunakan HashMap secara internal, lihat dokumen sumber

public class HashSet{
public HashSet() {
        map = new HashMap<>();
    }
}
SatyaS
sumber