get
”, “Metodların HashMap -da işləməsinin daxili mexanizmi nədir put
?”. Burada sadə bir nümunə ilə daxili funksionallığı izah etməyə çalışacağam. Çox nəzəriyyəyə girmədən, bir nümunə ilə başlayacağıq ki, siz daha yaxşı başa düşəsiniz və sonra metodların Java-da get
necə işlədiyini görə biləsiniz. put
Çox sadə bir misal götürək. Bizim sinifimiz var (ingiliscə “ölkə”), açar kimi sinif obyektini, dəyər kimi isə bu ölkənin paytaxtının adını Country
istifadə edəcəyik . Country
Aşağıda açar-dəyər cütünün hash xəritəsində necə saxlanacağını anlamağa kömək etmək üçün bir nümunə verilmişdir.
1. Ölkə.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;
}
}
Metodlar və bərabərliklər haqqında daha çox başa düşmək və öyrənmək istəyirsinizsə , bu linkihashcode
izləyə bilərsiniz .
2. HashMapStructure.java(əsas sinif)
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);
}
}
}
İndi kəsilmə nöqtəsini 23-cü sətirə təyin edin və run -> debug as-> java proqramını işə salın (tərcüməçinin qeydi - Eclipse üçün etibarlıdır). Proqram 23-cü sətirdə icranı dayandıracaq, bundan sonra countryCapitalMap üzərinə sağ klikləyin və watch seçin . Siz belə bir cədvəl görəcəksiniz: Burada aşağıdakıları görürük:
-
Entry[]
adlı 16 xanadan ibarət massiv vartable
; -
Bu massiv sinif obyektlərini saxlayır
Entry
. SinfinHashMap
daxili sinfi var -Entry
. Və bu sinfin nümunələri açar-dəyər cütləridir. Sinif quruluşuna nəzər salaqEntry
: -
Hər dəfə hash xəritəsində açar-dəyər cütlüyü yaratmağa çalışdığımız zaman həmin cütlük üçün sinif obyekti yaradılacaq
Entry
və o, yuxarıdakı cədvəldə saxlanılacaqEntry[]
. İndi isə bu obyektin məhz bu cədvəldə harada (hansı xanada) yazılacağı ilə maraqlanmalısınız. Açar-dəyər cütlüyündəki açar üçün hash kodu istifadə edərək hesablanırhashcode()
. Və bu hash kodu masanın hüceyrə nömrəsini hesablamaq üçün istifadə olunurEntry[]
; -
Entry
İndi cədvəlin 10-cu xanasına baxsanız, adlı bir sinif obyekti görəcəksinizHashMap$Entry
; - Biz 4 açar-dəyər cütü əlavə etdik, lakin massivdə yalnız 2 var!!! Bunun səbəbi, əgər 2 obyektin eyni hash kodu varsa, o zaman onlar eyni xanada saxlanılacaq. Bəs necə? Obyektlər əlaqəli siyahı ( ) kimi saxlanılacaq
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 имеет четное количество букв.
Aşağıdakı rəqəm əlaqəli siyahı ideyasını izah edəcək: İndi artıq hash xəritələrinin strukturu haqqında anlayışınız olduğundan, və üsullarına keçək . put
get
Qoy:
Bu metodun necə istifadə edildiyinə baxaq:/**
* Метод связывает указанное 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;
}
İndi bu kodu addım-addım anlamağa çalışaq:
-
key
Obyektin bərabərliyini yoxlayırıqnull
. Əgər belədirsə, o zaman obyektkey
yerdə saxlanacaq,table[0]
çünki hash kodunull
həmişə 0-dır; -
Sonra, biz onun hash kodunu hesablayacaq obyektin
key
metodunu çağırırıq.hashcode()
Bu hash kodu sinif obyektinin saxlanacağı massiv xanasını müəyyən etmək üçün istifadə olunurEntry
. Bəzən elə olur ki, bu funksiyahashcode
çox məharətlə yazılmır, ona görə də JDK tərtibatçılarıhash()
əvvəllər hesablanmış hash kodunu arqument kimi götürən fərqli funksiya - yaradıblar. Bu funksiya haqqında daha ətraflı oxumaq istəyirsinizsə, linki izləyə bilərsiniz ; -
indexFor(hash,table.length)
table
sinif obyektinin saxlanması üçün müəyyən ediləcəyi massivdə xüsusi xananı təyin etmək üçün istifadə olunurEntry
; -
Nümunəmizdə gördüyümüz kimi, əgər iki obyekt
key
eyni hash koduna malikdirsə (bu vəziyyət toqquşma kimi tanınır), onda onlar əlaqəli siyahı şəklində saxlanacaqlar. Buna görə də, bu mərhələdə siyahımızı təkrarlayırıq: -
yeni hesablanmış xana boşdursa, sinif obyekti
Entry
birbaşa bu xanada saxlanılacaq; -
next
əgər bu xana artıq hansısa obyekti ehtiva edirsə, onda sahəsi -ə bərabər olan elementə təkrarlanırnull
. Bundan sonra bizim sinif obyektimizEntry
siyahıda növbəti olur; -
eyni obyekti
key
yenidən əlavə etsək nə olacaq? Məntiqi olaraq, köhnə dəyəri əvəz etməlidir. Bəli, belə də olacaq.equals()
İterasiya zamanı düymələr ( ) metodu ilə müqayisə ediləcəkkey.equals(k)
. Nəticə doğrudursa, köhnə dəyər cari obyektin dəyəri ilə əvəz olunacaqEntry
.
Alın:
İndi isə metodun tətbiqinə nəzər salaq/**
* 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;
}
İndi siz put metodunun hasshmaplarda necə işlədiyini başa düşdüyünüzə görə, put metodunun necə işlədiyini başa düşmək
get
çox sadədir. Hash xəritəsindən dəyər əldə etmək üçün hər hansı bir açarı metoda ötürdüyünüz zaman:
-
Bir obyekt
Ekey bərabərlik üçün sınaqdan keçirilir null
. Əgər belədirsə, onda xanada saxlanılan obyektin dəyəri qaytarılacaqtable[0]
; -
hashcode()
Açar obyektin hash kodunu hesablayan adlı metodu var ; -
indexFor(hash,table.length)
table
sinif obyektinin götürüləcəyi xüsusi massiv xanasını müəyyən etmək üçün istifadə olunurEntry
; -
Massiv xana nömrəsini aldıqdan sonra
table
o, siyahıda təkrarlanacaq və metoddan istifadə edərək düymələri müqayisə edəcəkequals()
. Nəticə doğru olarsa, o zaman obyektin dəyəri qaytarılacaqEntry
, əks halda -null
.
Xatırlamaq lazım olanlar:
-
Sinfin açar-dəyər cütlərini saxlayan
HashMap
daxili sinfi var ;Entry
-
Sinfin obyektləri adlı
Entry
massivdə saxlanılır ;Entry[ ]
table
-
Massiv hüceyrəsi vedrə adlanır və əlaqəli siyahının ilk elementini saxlayır;
-
hashcode()
Obyekt metodukey
bu sinif obyektinin vedrəsini tapmaq üçün istifadə olunurEntry
; -
Əgər iki obyektin açarı eyni hash koduna malikdirsə, onlar eyni massiv kovasında saxlanılacaq
table
; -
equals()
Obyektin metodukey
onun unikallığını təsdiq etmək üçün istifadə olunur; -
Metodlar
equals()
vəhashcode()
obyektlərvalue
ümumiyyətlə istifadə edilmir.
GO TO FULL VERSION