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 를 선택합니다 . 다음과 같은 테이블이 표시됩니다. 여기서는 다음을 볼 수 있습니다.
-
Entry[]
이라는 이름의 16개 셀 배열이 있습니다table
. -
이 배열은 클래스의 개체를 저장합니다
Entry
. 클래스에는HashMap
내부 클래스가 있습니다Entry
. 그리고 이 클래스의 인스턴스는 키-값 쌍입니다. 클래스 구조를 살펴보겠습니다Entry
. -
해시 맵에서 키-값 쌍을 생성하려고 할 때마다 해당 쌍에 대한 클래스 개체가 생성되고
Entry
위의 테이블에 저장됩니다Entry[]
. 이제 이 개체가 이 테이블의 정확히 어디에(어느 셀에) 기록될 것인지 궁금할 것입니다. 키-값 쌍에 있는 키의 경우 해시 코드는hashcode()
. 그리고 이 해시 코드는 테이블 셀 번호를 계산하는 데 사용됩니다Entry[]
. -
이제 테이블의 셀 10을 보면
Entry
이라는 클래스 개체가 표시됩니다HashMap$Entry
. - 4개의 키-값 쌍을 추가했지만 배열에는 2개만 있습니다!!! 이는 두 개체의 해시 코드가 동일하면 동일한 셀에 저장되기 때문입니다. 하지만 어떻게? 객체는 연결리스트( )로 저장됩니다
LinkedList
.
static class Entry implements Map.Entry
{
final K key;
V value;
Entry next;
final int hash;
...//продолжение codeа
}
Hashcode for Japan = 95 так How длина слова Japan имеет нечетное количество букв.
Hashcode for India = 95 так How длина слова India имеет нечетное количество букв.
HashCode for Russia = 31 так How длина слова Russia имеет четное количество букв.
HashCode for France = 31 так How длина слова France имеет четное количество букв.
다음 그림은 연결 목록의 개념을 설명합니다. 이제 해시 맵의 구조에 대해 이해했으므로 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;
}
이제 이 코드를 단계별로 이해해 보겠습니다.
-
우리는 객체가
key
같은지 확인합니다null
. 그렇다면 해시 코드가 항상 0이기 때문에 객체는key
해당 위치에 저장됩니다 .table[0]
null
-
다음으로, 해시 코드를 계산하는 객체의
key
메소드를 호출합니다 .hashcode()
이 해시 코드는 클래스 객체가 저장될 배열 셀을 결정하는 데 사용됩니다Entry
. 때때로 이 함수가 매우 능숙하게 작성되지 않는 경우가 발생하므로 JDK 개발자는 이전에 계산된 해시 코드를 인수로 사용하는hashcode
다른 함수를 만들었습니다 .hash()
이 기능에 대해 더 자세히 알아보고 싶다면 링크를 따라 가세요 . -
indexFor(hash,table.length)
table
클래스 객체가 저장되도록 정의될 배열의 특정 셀을 정의하는 데 사용됩니다Entry
. -
예제에서 본 것처럼 두 개체가
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
매우 간단합니다. 해시 맵에서 값을 가져오기 위해 메서드에 키를 전달하는 경우:
-
객체
Ekey는 동등성 테스트를 거쳤습니다 null
. 그렇다면 셀에 저장된 객체의 값이 반환됩니다table[0]
. -
키 개체에는
hashcode()
해시 코드를 계산하는 메서드가 있습니다. -
indexFor(hash,table.length)
table
클래스 객체를 가져올 특정 배열 셀을 결정하는 데 사용됩니다Entry
. -
배열 셀 번호를 받은 후
table
목록을 반복하고 메서드를 사용하여 키를 비교합니다equals()
. 결과가 true이면 객체의 값이 반환되고Entry
, 그렇지 않으면 -가 반환됩니다null
.
기억해야 할 사항:
-
클래스에는 키-값 쌍을 저장하는
HashMap
내부 클래스가 있습니다 .Entry
-
클래스의 객체는 이라는
Entry
배열에 저장됩니다 .Entry[ ]
table
-
배열 셀은 버킷이라고 하며 연결 목록의 첫 번째 요소를 저장합니다.
-
hashcode()
객체 메소드는key
이 클래스 객체의 버킷을 찾는 데 사용됩니다Entry
. -
두 객체의 키가 동일한 해시 코드를 갖는 경우 동일한 배열 버킷에 저장됩니다
table
. -
equals()
개체의 메서드는 개체key
의 고유성을 확인하는 데 사용됩니다. -
메소드
equals()
와hashcode()
객체는value
전혀 사용되지 않습니다.
GO TO FULL VERSION