get
”, “ HashMapда методдор кантип иштешинин ички механизми кандай put
?”. Бул жерде мен жөнөкөй мисал аркылуу ички функцияларды түшүндүрүүгө аракет кылам. Өтө көп теорияга кирбестен, биз бир мисал менен баштайбыз, андыктан сиз жакшыраак түшүнүп, андан кийин Java'да методдор кандай иштээрин көрө get
аласыз put
. Келгиле, абдан жөнөкөй бир мисалды алалы. Бизде класс бар (англисче "өлкө"), ачкыч катары класс an objectисин, ал эми маани катары бул өлкөнүн борборунун атын 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ти оң баскыч менен чыкылдатып, саатты тандаңыз . Сиз төмөнкүдөй tableны көрөсүз: Бул жерде биз төмөнкүлөрдү көрөбүз:
-
Entry[]
аттуу 16 клеткадан турган массив барtable
; -
Бул массив класстын an objectтерин сактайт
Entry
. КласстынHashMap
ички классы бар -Entry
. Жана бул класстын мисалдары ачкыч-маани жуптары. Класстын түзүлүшүн карап көрөлүEntry
: -
Биз хэш картада ачкыч-маани жуптарын түзүүгө аракет кылган сайын, ал жуп үчүн класс an objectи түзүлөт
Entry
жана ал жогорудагы tableда сакталатEntry[]
. Эми сиз бул tableда бул an object так кайсы жерде (кайсы уячада) жазылат деп ойлонуп жатсаңыз керек. Ачкыч-маани жуптарындагы ачкыч үчүн хэш codeуhashcode()
. Жана бул хэш-code столдун клетка номерин эсептөө үчүн колдонулатEntry[]
; -
Эми tableнын 10-уячасын карасаңыз, класстын an objectисин
Entry
көрөсүзHashMap$Entry
; - Биз 4 ачкыч-маани жуптарын коштук, бирок массивде 2 гана бар!!! Себеби, эгерде 2 an objectтин хэш-codeу бирдей болсо, анда алар бир уячада сакталат. Бирок кантип? Объекттер шилтемеленген тизме ( ) катары сакталат
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;
}
Эми бул codeду этап-этабы менен түшүнүүгө аракет кылалы:
-
key
Объекттин теңдигин текшеребизnull
. Эгер ошондой болсо, анда an objectkey
жайгашкан жерде сакталат,table[0]
анткени хэш codeуnull
дайыма 0; -
Андан кийин, биз анын хэш codeун эсептей турган an objectтин
key
ыкмасын чакырабыз.hashcode()
Бул хэш-code класс an objectи сактала турган массив уячасын аныктоо үчүн колдонулатEntry
. Кээде бул функцияhashcode
өтө чебер жазылган эмес, ошондуктан JDK иштеп чыгуучуларhash()
аргумент катары мурда эсептелген хэш-codeду алган башка функцияны - түзүшкөн. Эгер сиз бул функция жөнүндө кененирээк окугуңуз келсе, шилтемеге өтсөңүз болот ; -
indexFor(hash,table.length)
table
класс an objectи сактала турган аныктала турган массивдеги белгилүү бир уячаны аныктоо үчүн колдонулатEntry
; -
Биздин мисалда көргөндөй, эки an objectтин
key
хэш-codeу бирдей болсо (бул жагдай кагылышуу катары белгилүү), анда алар байланышкан тизме түрүндө сакталат. Ошондуктан, бул этапта биз тизмени кайталайбыз: -
эгерде жаңы эсептелген уяча бош болсо, класс an objectи
Entry
түздөн-түз ушул уячага сакталат; -
next
эгерде бул уяча мурунтан эле кандайдыр бир an objectти камтыса, анда талаасы ге барабар болгон элементке кайталанатnull
. Андан кийин, биздин класс an objectиEntry
тизмеде кийинки болуп калат; -
ошол эле an objectти кайра кошсок эмне болот
key
? Логикалык жактан алганда, ал эски маанини алмаштыруу керек. Ооба, ошондой болот.equals()
Итерация учурунда баскычтар ( ) ыкмасы менен салыштырылатkey.equals(k)
. Эгерде натыйжа чын болсо, анда эски маани учурдагы an objectтин мааниси менен алмаштырылат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
абдан жөнөкөй. Хэш картадан маани алуу үчүн методго каалаган ачкычты өткөрүп бергенде:
-
Объект
Экей теңдик үчүн сыналат null
. Эгер ошондой болсо, анда уячада сакталган an objectтин мааниси кайтарылатtable[0]
; -
hashcode()
Негизги an objectтин хэш codeун эсептеген ыкмасы бар ; -
indexFor(hash,table.length)
table
класс an objectисин алуу үчүн белгилүү бир массив уячасын аныктоо үчүн колдонулатEntry
; -
Массив уячасынын номерин алгандан кийин,
table
ал тизме боюнча кайталанат жана методдун жардамы менен баскычтарды салыштыратequals()
. Эгерде натыйжа чын болсо, анда an objectтин мааниси кайтарылатEntry
, антпесе -null
.
Эсте турган нерселер:
-
Класста ачкыч-маани жуптарын сактаган
HashMap
ички класс бар ;Entry
-
Класстын an objectтери деп аталган
Entry
массивде сакталат ;Entry[ ]
table
-
Массив уячасы чака деп аталат жана байланышкан тизменин биринчи элементин сактайт;
-
hashcode()
Объект ыкмасыkey
бул класстын an objectинин чакасын табуу үчүн колдонулатEntry
; -
Эгерде эки an objectтин ачкычтары бирдей хэш-codeго ээ болсо, алар бир эле массив чакасында сакталат
table
; -
equals()
Объекттин ыкмасыkey
анын уникалдуулугун тастыктоо үчүн колдонулат; -
методдору
equals()
жанаhashcode()
an objectилериvalue
такыр колдонулbyte.
GO TO FULL VERSION