Cosa succede quando una chiave duplicata viene inserita in una HashMap?


276

Se mi passa lo stesso tasto più volte per HashMap's putmetodo, che cosa succede al valore originale? E se anche il valore si ripete? Non ho trovato alcuna documentazione su questo.

Caso 1: valori sovrascritti per una chiave

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

Abbiamo capito surely not one.

Caso 2: valore duplicato

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

Abbiamo capito one.

Ma cosa succede agli altri valori? Stavo insegnando le basi ad uno studente e mi è stato chiesto questo. È Mapcome un bucket in cui viene fatto riferimento all'ultimo valore (ma in memoria)?


7
A proposito, questa è un'ottima opportunità per mostrare la multi-hashmap che fa parte delle classi delle collezioni di Jakarta ( commons.apache.org/collections ). Ti permetterà di avere un numero qualsiasi di valori associati alla stessa chiave per quei momenti in cui ne hai bisogno.
John Munsch,

Risposte:


303

Per definizione, il putcomando sostituisce il valore precedente associato alla chiave specificata nella mappa (concettualmente come un'operazione di indicizzazione di array per tipi primitivi).

La mappa rilascia semplicemente il suo riferimento al valore. Se nient'altro contiene un riferimento all'oggetto, quell'oggetto diventa idoneo per la garbage collection. Inoltre, Java restituisce qualsiasi valore precedente associato alla chiave fornita (o nullse non presente), quindi è possibile determinare cosa c'era e mantenere un riferimento se necessario.

Maggiori informazioni qui: HashMap Doc


Grazie per questo. Leggendo però la documentazione Java questo non è menzionato chiaramente. Immagino che l'autore del documento abbia ipotizzato che si trattasse di un tacito presupposto di tutte le implementazioni della mappa hash.
Andrew S,

77

Puoi trovare la tua risposta nel javadoc di Map # put (K, V) (che in realtà restituisce qualcosa):

public V put(K key,
             V value)

Associa il valore specificato alla chiave specificata in questa mappa (operazione opzionale). Se la mappa conteneva in precedenza una mappatura per questa chiave, il vecchio valore viene sostituito dal valore specificato. (Si mdice che una mappa contenga una mappatura per una chiave kse e solo se m.containsKey(k)ritornasse true.)

Parametri:
key - chiave alla quale deve essere associato il valore specificato.
value- valore da associare alla chiave specificata.

Restituisce:
valore precedente associato alla chiave specificata o nullse non era presente alcun mapping key. (Un nullritorno può anche indicare che la mappa precedentemente associata nulla quella specificata key, se l'implementazione supporta i nullvalori.)

Quindi, se non si assegna il valore restituito durante la chiamata mymap.put("1", "a string"), diventa semplicemente non referenziato e quindi idoneo per la garbage collection.


3
Il valore restituito è il valore precedente (o null) come documentato appena sopra nel javadoc quindi, sì, questo è ciò che intendo. Può davvero essere male interpretato?
Pascal Thivent,

questo è molto utile
roottraveller,

18

Il valore precedente per la chiave viene eliminato e sostituito con quello nuovo.

Se desideri mantenere tutti i valori forniti da una chiave, potresti prendere in considerazione l'implementazione di qualcosa del genere:

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
Questa soluzione è deprivata. MultiHashMap fa parte di apache.commons.collections e non di java.
wikimix,

17

è la funzione Chiave / Valore e non è possibile avere una chiave duplicata per diversi valori perché quando si desidera ottenere il valore effettivo a quale dei valori appartiene la chiave inserita
nell'esempio quando si desidera ottenere il valore di "1" quale vero ?!
ecco i motivi per avere una chiave univoca per ogni valore ma potresti avere un trucco con la libreria standard 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);
    }
}


e potresti usarlo in questo modo:

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

e il risultato delle stampe sono:

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

Bella risposta! buon lavoro. Mi hai letteralmente salvato la vita in programmazione :).
Sottomissione Babu

Grazie anche a me! Ho dovuto aggiungere un metodo "rimuovi" per eseguire le stesse funzionalità di una normale mappa, ma ha funzionato alla grande!
JGlass

1
@JGlass il tuo benvenuto amico, ma questa non è una soluzione tecnica, questo è quello che puoi fare da java standard lib, nel problema tecnico devi essere attento al tuo problema, se hai bisogno di avere questo comportamento sono sicuro che non è una soluzione perché del concetto chiave / valore e deve essere pensato al problema e trovare un modo logico per risolverlo. comunque i miei dettagli sono solo divertenti da fare con java e in produzione, i problemi e il percorso di risoluzione sono molto diversi dal lavoro divertente! ma potresti usarlo quando il comportamento chiave / valore non è un tuo problema e scoprire di avere una struttura di dati come quella.
java acm,

13

Associa il valore specificato alla chiave specificata in questa mappa. Se la mappa in precedenza conteneva un mapping per la chiave, il vecchio valore viene sostituito.


12

Esso sostituisce il valore esistente nella mappa per il relativo tasto. E se non esiste una chiave con lo stesso nome, crea una chiave con il valore fornito. per esempio:

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

Tasto OUTPUT = "1", valore = "due"

Quindi, il valore precedente viene sovrascritto.


4

Alla tua domanda se la mappa fosse come un secchio: no.

È come un elenco con name=valuecoppie, mentre namenon deve essere una stringa (può, però).

Per ottenere un elemento, passi la tua chiave al metodo get () - che ti restituisce l'oggetto assegnato.

E una mappa hash significa che se stai provando a recuperare il tuo oggetto usando il metodo get, non confronterà l'oggetto reale con quello che hai fornito, perché avrebbe bisogno di scorrere il suo elenco e confrontare () la chiave hai fornito con l'elemento corrente.

Questo sarebbe inefficiente. Invece, indipendentemente dal contenuto del tuo oggetto, calcola un cosiddetto hashcode da entrambi gli oggetti e li confronta. È più facile confrontare due ints invece di due oggetti interi (possibilmente profondamente complessi). Puoi immaginare l'hashcode come un riepilogo con una lunghezza predefinita (int), quindi non è univoco e ha collisioni. Trovi le regole per l'hashcode nella documentazione a cui ho inserito il link.

Se vuoi saperne di più su questo, potresti voler dare un'occhiata agli articoli su javapractices.com e technofundo.com

Saluti


3

Ho sempre usato:

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

se volessi applicare più cose a una chiave identificativa.

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

}

potresti sempre fare qualcosa del genere e crearti un labirinto!

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

Le mappe di JDK non sono pensate per la memorizzazione di dati in chiavi duplicate.

  • Nella migliore delle ipotesi, il nuovo valore avrà la precedenza su quelli precedenti.

  • Lo scenario peggiore è un'eccezione (ad esempio quando si tenta di raccoglierlo come flusso):

Nessun duplicato:

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

Ok. Otterrai: $ 2 ==> {one = one}

Flusso duplicato:

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

Eccezione java.lang.IllegalStateException: chiave duplicata 1 (tentativo di unire i valori uno e non uno) | at Collectors.duplicateKeyException (Collectors.java:133) | su Collectors.lambda $ uniqKeysMapAccumulator $ 1 (Collectors.java:180) | su ReduceOps $ 3ReducingSink.accept (ReduceOps.java:169) | su Spliterators $ ArraySpliterator.forEachRemaining (Spliterators.java:948) | at AbstractPipeline.copyInto (AbstractPipeline.java:484) | at AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:474) | su ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:913) | at AbstractPipeline.evaluate (AbstractPipeline.java:234) | at ReferencePipeline.collect (ReferencePipeline.java:578) | at (# 4: 1)

Per gestire chiavi duplicate, utilizzare un altro pacchetto, ad esempio: https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html

Esistono molte altre implementazioni relative alle chiavi duplicate. Sono necessari per il Web (ad es. Chiavi cookie duplicate, le intestazioni Http possono avere gli stessi campi, ...)

In bocca al lupo! :)


l'operazione di "sostituzione" è costosa?
gaurav,

Può essere risolto solo usando JDK. Collectors.toMap()ha il terzo argomento: la funzione di unione. Se vogliamo ignorare semplicemente ultimo elemento duplicato: Stream.of("one", "two", "one").collect(Collectors.toMap(x -> x, x -> x, (key1, key2) -> key2)). link
stand alone

Anche il tuo secondo esempio di codice non è corretto. Questo input: "one", "not one", "surely not one"non produrrà alcun errore chiave duplicato a causa di stringhe diverse.
stand alone

Ciao @ stare in piedi da solo. Leggere attentamente la funzione di mappatura (toMap).
Witold Kaczurba,

Ciao @WitoldKaczurba. Si prega di compilare il codice prima di pubblicare.
stand alone


1

Sì, questo significa che tutte le 1 chiavi con valore vengono sovrascritte con l'ultimo valore aggiunto e qui aggiungi "sicuramente non uno", quindi verrà visualizzato solo "sicuramente non uno".

Anche se stai provando a visualizzare con un ciclo, visualizzerà anche solo una chiave e un valore che hanno la stessa chiave.


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

Significa che la mappa hash non consentirà i duplicati, se hai correttamente eguagliato i metodi uguale e hashCode ().

HashSet utilizza anche HashMap internamente, consultare il documento di origine

public class HashSet{
public HashSet() {
        map = new HashMap<>();
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.