JavaRush /Java Blog /Random-TW /HashMap 在 Java 中如何運作?
gnev
等級 24

HashMap 在 Java 中如何運作?

在 Random-TW 群組發布
HashMap 在 Java 中如何運作? - 1在面試中,他們經常會問「HashMap 在 Java 中如何運作?」之類的問題。get”,“HashMap中方法工作的內部機制是什麼put?”。在這裡我將嘗試使用一個簡單的範例來解釋內部功能。在不涉及太多理論的情況下,我們將從一個範例開始,以便您可以更好地理解並了解方法在 Java 中的工作get原理。put讓我們舉一個非常簡單的例子。我們有一個類別Country(英語“國家”),我們將使用類別物件Country作為鍵,並使用該國家的首都名稱作為值。下面的例子可以幫助我們理解鍵值對如何儲存在雜湊圖中。

1. 國家.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;
 }
}
如果您想了解和了解有關方法hashcode和等於的更多信息,可以點擊此連結

2.HashMapStructure.java(主類別)

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);
            }
        }
}
現在將斷點設定在第23行並執行run -> debug as -> java application(譯者註-對Eclipse有效)。程式將在第 23 行停止執行,之後右鍵單擊CountryCapitalMap並選擇watch。您將看到這樣的表格: HashMap 在 Java 中如何運作? - 2這裡我們看到以下內容:
  1. 有一個包含Entry[]16 個單元格的數組,名為table

  2. 該數組儲存該類別的物件Entry。該類別HashMap有一個內部類別 - Entry。該類別的實例是鍵值對。讓我們來看看類別結構Entry

  3. static class Entry implements Map.Entry
            {
                    final K key;
                    V value;
                    Entry next;
                    final int hash;
                    ...//продолжение codeа
            }
  4. 每次我們嘗試在哈希映射中創建鍵值對時,都會為該對創建一個類對象Entry,並將其存儲在上表中Entry[]。現在您應該想知道該物件將準確寫入該表中的哪個位置(在哪個儲存格中)。對於鍵值對中的鍵,使用 計算雜湊碼hashcode()。這個哈希碼用於計算表單元格數量Entry[]

  5. 現在,如果您查看表格的儲存格 10,您將看到一個Entry名為 的類別物件HashMap$Entry

  6. 我們新增了 4 個鍵值對,但數組中只有 2 個!!!這是因為如果兩個物件具有相同的雜湊碼,那麼它們將儲存在同一個儲存格中。但如何呢?物件將被儲存為鍊錶(LinkedList)。
以下是如何計算鍵值對的雜湊碼。
Hashcode for Japan = 95 так How длина слова Japan имеет нечетное количество букв.
Hashcode for India = 95 так How длина слова India имеет нечетное количество букв.
HashCode for Russia = 31 так How длина слова Russia имеет четное количество букв.
HashCode for France = 31 так How длина слова France имеет четное количество букв.
下圖將解釋鍊錶的思想: HashMap 在 Java 中如何運作? - 3現在您已經了解了哈希映射的結構,讓我們繼續討論put和方法get

放:

讓我們來看看這個方法是如何使用的:
/**
  * Метод связывает указанное 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;
 }
現在讓我們試著逐步理解這段程式碼:
  1. 我們檢查物件key是否相等null。如果是,則該物件key將儲存在該位置table[0],因為 的雜湊碼null始終為 0;

  2. 接下來,我們呼叫該物件的key方法hashcode(),該方法將計算其雜湊碼。此雜湊碼用於確定將儲存類別物件的陣列單元Entry。有時碰巧這個函數hashcode寫得不太熟練,於是JDK開發者創建了一個不同的函數—— hash(),它以之前計算的雜湊碼作為參數。如果您有興趣更詳細地了解此功能,可以點擊連結

  3. indexFor(hash,table.length)用於定義數組中的特定單元格,table其中將定義要儲存的類別物件Entry

  4. 正如我們在範例中看到的,如果兩個物件key具有相同的雜湊碼(這種情況稱為衝突),那麼它們將以鍊錶的形式儲存。因此,在這個階段我們迭代我們的列表:

    • 如果新計算的儲存格為空,則類別物件Entry將直接儲存到該儲存格中;

    • next如果此單元格已包含某個對象,則迭代到字段等於 的元素null。之後,我們的類別物件Entry成為列表中的下一個;

    • 如果我們再次加入同一個物件怎麼辦key?從邏輯上講,它應該替換舊值。是的,會這樣。equals()在迭代過程中,將使用( ) 方法對鍵進行比較key.equals(k)。如果結果為 true,則舊值將被目前物件的值取代Entry

得到:

現在我們來看看該方法的應用 得到
/**
  * 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;
 }
現在您已經了解了 put 方法在雜湊圖中的工作原理,理解 put 方法的工作原理就 get非常簡單了。當您將任何鍵傳遞給方法以從哈​​希映射中獲取值時:
  1. 一個東西 Ekey 被測試是否相等 null。如果是,則傳回儲存在儲存格中的物件的值 table[0]

  2. 密鑰物件有一個稱為hashcode()計算哈希碼的方法;

  3. indexFor(hash,table.length)table用於確定從中獲取類別物件的特定數組單元Entry

  4. 收到數組單元格編號後,table它將迭代列表並使用方法比較鍵equals()。如果結果為 true,則傳回該物件的值Entry,否則傳回 - null

要記住的事:

  • 該類別HashMap有一個Entry儲存鍵值對的內部類別;

  • 該類別的物件Entry儲存在一個Entry[ ]名為 的陣列中table

  • 數組單元稱為桶,儲存鍊錶的第一個元素;

  • hashcode()object方法key用於尋找該類別物件的桶Entry

  • 如果兩個物件的key具有相同的雜湊碼,則它們將被儲存在同一個陣列桶中table

  • equals()對象的方法key用以確認其唯一性;

  • 根本不使用方法equals()hashcode()物件。value

來源
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION