JavaRush /Blog Java /Random-PL /Jak HashMap działa w Javie?
gnev
Poziom 24

Jak HashMap działa w Javie?

Opublikowano w grupie Random-PL
Jak HashMap działa w Javie?  - 1Bardzo często podczas rozmów kwalifikacyjnych zadają pytania typu „ Jak działa HashMap w Javie?” ”, „Jaki jest wewnętrzny mechanizm działania metod getw putHashMap?”. Tutaj postaram się wyjaśnić wewnętrzną funkcjonalność na prostym przykładzie. Nie wdając się zbytnio w teorię, zaczniemy od przykładu, abyś mógł lepiej zrozumieć, a następnie zobaczyć, jak metody działają getrównież putw Javie . Weźmy bardzo prosty przykład. Mamy klasę Country(angielski „kraj”), użyjemy obiektu klasy Countryjako klucza, a nazwę stolicy tego kraju jako wartości. Poniżej znajduje się przykład, który pomoże nam zrozumieć, w jaki sposób para klucz-wartość będzie przechowywana na mapie skrótów.

1. Kraj.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;
 }

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

 @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;
 }
}
Jeśli chcesz zrozumieć i dowiedzieć się więcej na temat metod hashcodei równości, możesz kliknąć ten link .

2. HashMapStructure.java (klasa główna)

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);
            }
        }
}
Teraz ustaw punkt przerwania na linię 23 i uruchom polecenie run -> debug as-> aplikacja Java (uwaga tłumacza - dotyczy Eclipse). Program zatrzyma wykonywanie w linii 23, po czym kliknij prawym przyciskiem myszy countryCapitalMap i wybierz opcję watch . Zobaczysz następującą tabelę: Jak HashMap działa w Javie?  - 2Tutaj widzimy, co następuje:
  1. Istnieje tablica Entry[]złożona z 16 komórek o nazwach table;

  2. Tablica ta przechowuje obiekty klasy Entry. Klasa HashMapma klasę wewnętrzną - Entry. Instancje tej klasy to pary klucz-wartość. Przyjrzyjmy się strukturze klas Entry:

  3. static class Entry implements Map.Entry
            {
                    final K key;
                    V value;
                    Entry next;
                    final int hash;
                    ...//продолжение kodа
            }
  4. Za każdym razem, gdy spróbujemy utworzyć parę klucz-wartość na mapie skrótów, dla tej pary zostanie utworzony obiekt klasy, Entryktóry będzie przechowywany w powyższej tabeli Entry[]. A teraz powinieneś się zastanawiać, gdzie dokładnie w tej tabeli ten obiekt zostanie zapisany (w której komórce). W przypadku klucza w parze klucz-wartość kod skrótu jest obliczany przy użyciu metody hashcode(). Ten kod skrótu jest używany do obliczenia numeru komórki tabeli Entry[];

  5. Teraz, jeśli spojrzysz na komórkę 10 tabeli, zobaczysz obiekt klasy Entryo nazwie HashMap$Entry;

  6. Dodaliśmy 4 pary klucz-wartość, ale w tablicy są tylko 2!!! Dzieje się tak dlatego, że jeśli 2 obiekty mają ten sam kod skrótu, wówczas będą przechowywane w tej samej komórce. Ale jak? Obiekty będą przechowywane jako lista połączona ( LinkedList).
Oto jak zostanie obliczony kod skrótu dla naszych par klucz-wartość.
Hashcode for Japan = 95 так Jak длина слова Japan имеет нечетное количество букв.
Hashcode for India = 95 так Jak длина слова India имеет нечетное количество букв.
HashCode for Russia = 31 так Jak длина слова Russia имеет четное количество букв.
HashCode for France = 31 так Jak длина слова France имеет четное количество букв.
Poniższy rysunek wyjaśni ideę listy połączonej: Jak HashMap działa w Javie?  - 3Teraz, gdy masz już wiedzę na temat struktury map skrótów, przejdźmy do metod puti get.

Umieścić:

Zobaczmy, jak stosowana jest ta metoda:
/**
  * Метод связывает указанное oznaczający с указанным ключом в данной хэш-карте. Если
  * карта до этого уже содержала некоторое oznaczający, соответствующее этому ключу,
  * то старое oznaczający заменяется на указанное.
  * @param key
  *            ключ, с которым связывается указанное oznaczający
  * @param value
  *            oznaczający, связываемое с указанным ключом
  * @возвращает oznaczający связанное с <tt>ключом</tt>, Lub <tt>null</tt>,
  *         если ниJakое oznaczający не соответствует <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;
 }
Spróbujmy teraz zrozumieć ten kod krok po kroku:
  1. Sprawdzamy obiekt keypod kątem równości null. Jeśli tak, to obiekt keyzostanie zapisany w lokalizacji, table[0]ponieważ kod skrótu dla nullma zawsze wartość 0;

  2. keyNastępnie wywołujemy metodę obiektu hashcode(), która obliczy jego kod skrótu. Ten kod skrótu służy do określenia komórki tablicy, w której będzie przechowywany obiekt klasy Entry. Czasami zdarza się, że funkcja ta hashcodenie jest napisana zbyt umiejętnie, dlatego twórcy JDK stworzyli inną funkcję - hash(), która jako argument przyjmuje wcześniej obliczony kod skrótu. Jeśli chcesz przeczytać więcej o tej funkcji, kliknij link ;

  3. indexFor(hash,table.length)używany do zdefiniowania konkretnej komórki w tablicy, tablew której zdefiniowany zostanie obiekt klasy, który ma być przechowywany Entry;

  4. Jak widzieliśmy w naszym przykładzie, jeśli dwa obiekty keymają ten sam kod skrótu (taka sytuacja jest nazywana kolizją), to zostaną one zapisane w formie połączonej listy. Dlatego na tym etapie iterujemy naszą listę:

    • jeżeli nowo obliczona komórka jest pusta, to obiekt klasy Entryzostanie zapisany bezpośrednio w tej komórce;

    • jeśli ta komórka zawiera już jakiś obiekt, iteruje do elementu, którego pole jest nextrówne null. Następnie obiekt naszej klasy Entrybędzie następny na liście;

    • keyco jeśli ponownie dodamy ten sam obiekt ? Logicznie rzecz biorąc, powinna zastąpić starą wartość. Tak, tak będzie. W trakcie iteracji klucze będą porównywane metodą equals()( key.equals(k)). Jeśli wynik będzie prawdziwy, wówczas stara wartość zostanie zastąpiona wartością bieżącego obiektu Entry.

Dostawać:

Przyjrzyjmy się teraz zastosowaniu tej metody Dostawać
/**
  * zwroty oznaczający, которое соответствует указанному ключу, Lub {@code null}, если
  * данная карта не содержит пары с указанным ключом.
  *
  *
  * <p>
  * Более точно, если в данной карте содержится такой ключ {@code k}
  * с соответствующим ему oznaczającyм {@code v}, что {@code (key==null ? k==null : key.equals(k))},
  * то метод возвращает {@code v}; в противном случае возвращается {@code null}.
  * (может быть не более одной такой пары)
  *
  * </p><p>
  * Возвращенное oznaczający {@code null} не <i>обязательно</i> говорит о том, что
  * в карте нет пары с таким указанным ключом; а возможно, что в карте однозначно
  * указано соответствие этого ключа со oznaczającyм {@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;
 }
Teraz, gdy już wiesz, jak działa metoda put w mapach skrótów, zrozumienie, jak działa metoda put, jest getbardzo proste. Kiedy przekazujesz dowolny klucz do metody, aby uzyskać wartość z mapy skrótów:
  1. Obiekt Ekey jest testowany pod kątem równości null. Jeżeli tak, to zwrócona zostanie wartość obiektu zapisanego w komórce table[0];

  2. Obiekt klucza ma metodę hashcode(), która oblicza kod skrótu;

  3. indexFor(hash,table.length)używany do określenia konkretnej komórki tablicy, tablez której ma zostać pobrany obiekt klasy Entry;

  4. Po otrzymaniu numeru komórki tablicy tablebędzie iterował po liście i porównał klucze metodą equals(). Jeżeli wynik będzie prawdziwy to zwrócona zostanie wartość obiektu Entry, w przeciwnym wypadku - null.

Rzeczy do zapamiętania:

  • Klasa HashMapma klasę wewnętrzną Entry, która przechowuje pary klucz-wartość;

  • Obiekty klasy Entryprzechowywane są w tablicy Entry[ ]zwanej table;

  • Komórka tablicowa nazywana jest wiadrem i przechowuje pierwszy element połączonej listy;

  • Do znalezienia zasobnika obiektu tej klasy używana jest metoda hashcode()obiektowa ;keyEntry

  • Jeśli klucze dwóch obiektów mają ten sam kod skrótu, będą one przechowywane w tym samym wiadrze tablicy table;

  • Metoda equals()obiektu keysłuży do potwierdzenia jego unikalności;

  • Metody equals()i hashcode()obiekty valuenie są w ogóle używane.

Źródło
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION