JavaRush /จาวาบล็อก /Random-TH /HashMap ทำงานอย่างไรใน Java?
gnev
ระดับ

HashMap ทำงานอย่างไรใน Java?

เผยแพร่ในกลุ่ม
HashMap ทำงานอย่างไรใน Java?  - 1บ่อยครั้งในการสัมภาษณ์ พวกเขาถามคำถามเช่น “ HashMap ทำงานอย่างไรใน Java?” ”, “กลไกภายในของวิธีการทำงานgetในputHashMap คืออะไร” ที่นี่ฉันจะพยายามอธิบายฟังก์ชันการทำงานภายในโดยใช้ตัวอย่างง่ายๆ เราจะเริ่มต้นด้วยตัวอย่างเพื่อให้คุณเข้าใจได้ดีขึ้น และดูว่าเมธอดต่างๆ ทำงานอย่างไรใน 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 (หมายเหตุของผู้แปล - ใช้ได้กับ 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 คู่ในอาร์เรย์!!! เนื่องจากหากวัตถุ 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หลังจากนี้ class object ของเราEntryจะกลายเป็นรายการถัดไปในรายการ

    • จะเกิดอะไรขึ้นถ้าเราเพิ่มวัตถุเดียวกันkeyอีกครั้ง? ตามหลักเหตุผลแล้ว ควรแทนที่ค่าเก่า ใช่ มันจะเป็นเช่นนั้น ในระหว่างการวนซ้ำ คีย์จะถูกเปรียบเทียบโดยใช้ วิธี equals()( key.equals(k)) หากผลลัพธ์เป็นจริง ค่าเก่าจะถูกแทนที่ด้วยค่าของอ็อบเจ็กต์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;
 }
ตอนนี้คุณเข้าใจแล้วว่าวิธีใส่ทำงานอย่างไรในแฮชแมปแล้ว การทำความเข้าใจว่าวิธีใส่ทำงานอย่างไรนั้น getง่ายมาก เมื่อคุณส่งคีย์ใด ๆ ไปยังวิธีการรับค่าจากแมปแฮช:
  1. วัตถุ Ekey ได้รับการทดสอบเพื่อความเท่าเทียม nullกัน หากเป็นเช่นนั้น ค่าของออบเจ็กต์ที่เก็บไว้ในเซลล์จะถูกส่ง table[0]กลับ

  2. คีย์ออบเจ็กต์มีวิธีการที่เรียกว่าhashcode()คำนวณรหัสแฮช

  3. indexFor(hash,table.length)ใช้เพื่อกำหนดเซลล์อาร์เรย์เฉพาะtableที่จะใช้วัตถุEntryคลาส

  4. หลังจากได้รับหมายเลขเซลล์อาร์เรย์แล้วtableจะวนซ้ำรายการและเปรียบเทียบคีย์โดยใช้equals()เมธอด หากผลลัพธ์เป็นจริง ค่าของอ็อบเจ็กต์จะถูกส่งกลับEntryมิฉะนั้นnull-

สิ่งที่ต้องจำ:

  • คลาสHashMapมีคลาสภายในEntryที่เก็บคู่คีย์-ค่า

  • วัตถุของคลาสEntryจะถูกเก็บไว้ในอาร์เรย์Entry[ ]ที่เรียกว่าtable;

  • เซลล์อาร์เรย์เรียกว่าที่เก็บข้อมูลและจัดเก็บองค์ประกอบแรกของรายการที่เชื่อมโยง

  • วิธีhashcode()การวัตถุkeyใช้ในการค้นหาถังของวัตถุคลาสEntryนี้

  • หากคีย์ของวัตถุสองตัวมีรหัสแฮชเหมือนกัน คีย์เหล่านั้นจะถูกจัดเก็บไว้ในที่เก็บข้อมูลอาร์เรย์tableเดียวกัน

  • วิธีการequals()ของวัตถุkeyใช้เพื่อยืนยันเอกลักษณ์ของมัน

  • วิธีการequals()และhashcode()วัตถุvalueไม่ได้ใช้เลย

แหล่งที่มา
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION