JavaRush /Java Blog /Random-IT /Come funziona HashMap in Java?
gnev
Livello 24

Come funziona HashMap in Java?

Pubblicato nel gruppo Random-IT
Come funziona HashMap in Java?  -1Molto spesso nelle interviste vengono poste domande del tipo " Come funziona HashMap in Java?" ”, “Qual è il meccanismo interno di come funzionano i metodi getin putHashMap?”. Qui cercherò di spiegare la funzionalità interna utilizzando un semplice esempio. Senza addentrarci troppo nella teoria, inizieremo con un esempio in modo che possiate capire meglio e poi vedere come funzionano i metodi getanche putin Java . Facciamo un esempio molto semplice. Abbiamo una classe Country("paese" inglese), utilizzeremo l'oggetto della classe Countrycome chiave e il nome della capitale di questo paese come valore. Di seguito è riportato un esempio per aiutarci a capire come verrà archiviata una coppia chiave-valore in una mappa hash.

1. Paese.java

package org.arpit.javapostsforlearning;
public class Country {

 String name;
 long population;

 public Country(String name, long population) {
  super();
  this.name = name;
  this.population = population;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public long getPopulation() {
  return population;
 }
 public void setPopulation(long population) {
  this.population = population;
 }

 // если длина имени в an objectе Country - четное число,
 // то возвращаем 31(любое случайное число), а если нечетное - 95 (любое случайное число).
 // указанный ниже метод - это не самый лучший способ генерации хеш-codeа,
 // но мы воспользуемся им для более четкого понимания хеш-карт.

 @Override
 public int hashCode() {
  if(this.name.length()%2==0)
   return 31;
  else
   return 95;
 }
 @Override
 public boolean equals(Object obj) {

  Country other = (Country) obj;
   if (name.equalsIgnoreCase((other.name)))
   return true;
  return false;
 }
}
Se vuoi capire e saperne di più su metodi hashcodeed eguali, puoi seguire questo link .

2. HashMapStructure.java (classe principale)

import java.util.HashMap;
import java.util.Iterator;

public class HashMapStructure {

    /**
     * @author Arpit Mandliya
     */
    public static void main(String[] args) {

        Country india=new Country("India",1000);
        Country japan=new Country("Japan",10000);

        Country france=new Country("France",2000);
        Country russia=new Country("Russia",20000);

        HashMap<country,string> countryCapitalMap=new HashMap<country,string>();
        countryCapitalMap.put(india,"Delhi");
        countryCapitalMap.put(japan,"Tokyo");
        countryCapitalMap.put(france,"Paris");
        countryCapitalMap.put(russia,"Moscow");

        Iterator<country> countryCapitalIter=countryCapitalMap.keySet().iterator();//установите
        //debug-точку на этой строке(23)
        while(countryCapitalIter.hasNext())
        {
            Country countryObj=countryCapitalIter.next();
            String capital=countryCapitalMap.get(countryObj);
            System.out.println(countryObj.getName()+"----"+capital);
            }
        }
}
Ora imposta il punto di interruzione sulla riga 23 ed esegui run -> debug as-> java application (nota del traduttore - valido per Eclipse). Il programma interromperà l'esecuzione alla riga 23, dopodiché fare clic con il tasto destro su countryCapitalMap e selezionare watch . Vedrai una tabella come questa: Come funziona HashMap in Java?  - 2Qui vediamo quanto segue:
  1. Esiste un array Entry[]di 16 celle denominato table;

  2. Questo array memorizza gli oggetti della classe Entry. La classe HashMapha una classe interna - Entry. E le istanze di questa classe sono coppie chiave-valore. Diamo un'occhiata alla struttura della classe Entry:

  3. static class Entry implements Map.Entry
            {
                    final K key;
                    V value;
                    Entry next;
                    final int hash;
                    ...//продолжение codeа
            }
  4. Ogni volta che proviamo a creare una coppia chiave-valore in una mappa hash, verrà creato un oggetto classe per quella coppia Entrye verrà archiviato nella tabella sopra Entry[]. E ora ti starai chiedendo dove esattamente in questa tabella verrà scritto questo oggetto (in quale cella). Per una chiave in una coppia chiave-valore, un codice hash viene calcolato utilizzando il metodo hashcode(). E questo codice hash viene utilizzato per calcolare il numero di celle della tabella Entry[];

  5. Ora se guardi la cella 10 della tabella, vedrai un oggetto di classe Entrydenominato HashMap$Entry;

  6. Abbiamo aggiunto 4 coppie chiave-valore, ma ce ne sono solo 2 nell'array!!! Questo perché se 2 oggetti hanno lo stesso codice hash, verranno archiviati nella stessa cella. Ma come? Gli oggetti verranno archiviati come elenco collegato ( LinkedList).
Ecco come verrà calcolato il codice hash per le nostre coppie chiave-valore.
Hashcode for Japan = 95 так How длина слова Japan имеет нечетное количество букв.
Hashcode for India = 95 так How длина слова India имеет нечетное количество букв.
HashCode for Russia = 31 так How длина слова Russia имеет четное количество букв.
HashCode for France = 31 так How длина слова France имеет четное количество букв.
La figura seguente spiegherà l'idea di un elenco collegato: Come funziona HashMap in Java?  - 3Ora che hai già compreso la struttura delle mappe hash, passiamo ai metodi pute get.

Mettere:

Vediamo come si utilizza questo metodo:
/**
  * Метод связывает указанное meaning с указанным ключом в данной хэш-карте. Если
  * карта до этого уже содержала некоторое meaning, соответствующее этому ключу,
  * то старое meaning заменяется на указанное.
  * @param key
  *            ключ, с которым связывается указанное meaning
  * @param value
  *            meaning, связываемое с указанным ключом
  * @возвращает meaning связанное с <tt>ключом</tt>, or <tt>null</tt>,
  *         если ниHowое meaning не соответствует <tt>ключу</tt>. ( Возврат <tt>null</tt>
  *         может так же говорить о том, что в карте заведомо <tt>null</tt> был связан с
  *         <tt>ключом</tt>.)
  */
 public V put(K key, V value) {
  if (key == null)
   return putForNullKey(value);
  int hash = hash(key.hashCode());
  int i = indexFor(hash, table.length);
  for (Entry<k , V> e = table[i]; e != null; e = e.next) {
   Object k;
   if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    V oldValue = e.value;
    e.value = value;
    e.recordAccess(this);
    return oldValue;
   }
  }

  modCount++;
  addEntry(hash, key, value, i);
  return null;
 }
Ora proviamo a capire passo dopo passo questo codice:
  1. Controlliamo l' keyuguaglianza dell'oggetto null. In tal caso, l'oggetto keyverrà archiviato nella posizione table[0]perché il codice hash nullè sempre 0;

  2. Successivamente, chiamiamo il keymetodo dell'oggetto hashcode(), che calcolerà il suo codice hash. Questo codice hash viene utilizzato per determinare la cella dell'array in cui verrà archiviato l'oggetto della classe Entry. A volte capita che questa funzione hashcodenon sia scritta in modo molto abile, quindi gli sviluppatori JDK hanno creato una funzione diversa - hash(), che prende come argomento il codice hash precedentemente calcolato. Se sei interessato a leggere più in dettaglio questa funzione, puoi seguire il collegamento ;

  3. indexFor(hash,table.length)utilizzato per definire una cella specifica nell'array tablein cui verrà definito un oggetto di classe da archiviare Entry;

  4. Come abbiamo visto nel nostro esempio, se due oggetti keyhanno lo stesso codice hash (questa situazione è nota come collisione), verranno archiviati sotto forma di un elenco collegato. Pertanto, in questa fase iteriamo la nostra lista:

    • se la cella appena calcolata è vuota, l'oggetto della classe Entryverrà salvato direttamente in questa cella;

    • se questa cella contiene già qualche oggetto, esegue l'iterazione sull'elemento il cui campo è nextuguale a null. Successivamente, il nostro oggetto classe Entrydiventa il prossimo nell'elenco;

    • cosa succede se aggiungiamo keydi nuovo lo stesso oggetto? Logicamente, dovrebbe sostituire il vecchio valore. Sì, sarà così. Durante l'iterazione, le chiavi verranno confrontate utilizzando il metodo equals()( key.equals(k)). Se il risultato è vero, il vecchio valore verrà sostituito con il valore dell'oggetto corrente Entry.

Ottenere:

Ora diamo un'occhiata all'applicazione del metodo Ottenere
/**
  * returns meaning, которое соответствует указанному ключу, or {@code null}, если
  * данная карта не содержит пары с указанным ключом.
  *
  *
  * <p>
  * Более точно, если в данной карте содержится такой ключ {@code k}
  * с соответствующим ему meaningм {@code v}, что {@code (key==null ? k==null : key.equals(k))},
  * то метод возвращает {@code v}; в противном случае возвращается {@code null}.
  * (может быть не более одной такой пары)
  *
  * </p><p>
  * Возвращенное meaning {@code null} не <i>обязательно</i> говорит о том, что
  * в карте нет пары с таким указанным ключом; а возможно, что в карте однозначно
  * указано соответствие этого ключа со meaningм {@code null}.
  * Можно воспользоваться операцией {@link #containsKey containsKey}, чтобы
  * отличить эти два случая
  * @see #put(Object, Object)
  */
 public V get(Object key) {
  if (key == null)
   return getForNullKey();
  int hash = hash(key.hashCode());
  for (Entry<k , V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) {
   Object k;
   if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
    return e.value;
  }
  return null;
 }
Ora che hai capito come funziona il metodo put nelle hashmap, capire come funziona il metodo put è getmolto semplice. Quando passi una chiave qualsiasi a un metodo per ottenere un valore da una mappa hash:
  1. Un oggetto Ekey viene testato per l'uguaglianza null. In tal caso verrà restituito il valore dell'oggetto memorizzato nella cella table[0];

  2. L'oggetto chiave ha un metodo chiamato hashcode()che calcola il codice hash;

  3. indexFor(hash,table.length)utilizzato per determinare una cella specifica dell'array tableda cui prendere un oggetto di classe Entry;

  4. Dopo aver ricevuto il numero di cella dell'array, tablescorrerà l'elenco e confronterà le chiavi utilizzando il metodo equals(). Se il risultato è vero, verrà restituito il valore dell'oggetto Entry, altrimenti - null.

Cose da ricordare:

  • La classe HashMapha una classe interna Entryche memorizza le coppie chiave-valore;

  • Gli oggetti della classe Entrysono memorizzati in un array Entry[ ]chiamato table;

  • Una cella dell'array è chiamata bucket e memorizza il primo elemento di un elenco collegato;

  • Il metodo hashcode()dell'oggetto keyviene utilizzato per trovare il bucket di questo oggetto di classe Entry;

  • Se le chiavi di due oggetti hanno lo stesso codice hash, verranno archiviate nello stesso bucket di array table;

  • Il metodo equals()di un oggetto keyviene utilizzato per confermarne l'unicità;

  • Metodi equals()e hashcode()oggetti valuenon vengono utilizzati affatto.

Fonte
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION