JavaRush /Java Blog /Random-KO /HashMap은 Java에서 어떻게 작동합니까?
gnev
레벨 24

HashMap은 Java에서 어떻게 작동합니까?

Random-KO 그룹에 게시되었습니다
HashMap은 Java에서 어떻게 작동합니까?  - 1인터뷰에서 그들은 “ HashMap은 Java에서 어떻게 작동하나요?” 와 같은 질문을 자주 합니다. get”, “HashMap 에서 메소드가 작동하는 방식에 대한 내부 메커니즘은 무엇입니까 put?”. 여기서는 간단한 예를 들어 내부 기능을 설명하려고 합니다. 너무 많은 이론을 다루지 않고 예제부터 시작하여 Java에서 메소드가 어떻게 작동하는지 더 잘 이해할 수 있도록 하겠습니다 get. put아주 간단한 예를 들어보겠습니다. 우리는 클래스(영어 "국가")를 가지고 있으며 클래스 객체를 키로 Country사용 하고 이 나라의 수도 이름을 값으로 사용합니다. Country다음은 키-값 쌍이 해시 맵에 저장되는 방법을 이해하는 데 도움이 되는 예입니다.

1. 컨트리.자바

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행으로 설정하고 실행 -> 다음으로 디버그 -> 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개만 있습니다!!! 이는 두 개체의 해시 코드가 동일하면 동일한 셀에 저장되기 때문입니다. 하지만 어떻게? 객체는 연결리스트( )로 저장됩니다 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에서 어떻게 작동합니까?  - 삼이제 해시 맵의 구조에 대해 이해했으므로 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. 그렇다면 해시 코드가 항상 0이기 때문에 객체는 key해당 위치에 저장됩니다 .table[0]null

  2. 다음으로, 해시 코드를 계산하는 객체의 key메소드를 호출합니다 . hashcode()이 해시 코드는 클래스 객체가 저장될 배열 셀을 결정하는 데 사용됩니다 Entry. 때때로 이 함수가 매우 능숙하게 작성되지 않는 경우가 발생하므로 JDK 개발자는 이전에 계산된 해시 코드를 인수로 사용하는 hashcode다른 함수를 만들었습니다 . 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()객체 메소드는 key이 클래스 객체의 버킷을 찾는 데 사용됩니다 Entry.

  • 두 객체의 키가 동일한 해시 코드를 갖는 경우 동일한 배열 버킷에 저장됩니다 table.

  • equals()개체의 메서드는 개체 key의 고유성을 확인하는 데 사용됩니다.

  • 메소드 equals()hashcode()객체는 value전혀 사용되지 않습니다.

원천
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION