Middle
Ինչպես է կառուցված և կազմակերպված Data Structure HashMap-ը Java-ում:
sobes.tech AI
Պատասխան AI-ից
HashMap-ը Java-ում հիմնված է հեշավորման սկզբունքների վրա։ Այն պահում է « բանալի-արժեք » զույգեր։
Ներքին կառուցվածքը:
- Բաքուների (կոնտեյներների) զանգված։ Յուրաքանչյուր բաքը կապված ցանկ է (կամ ծառ, Java 8-ից սկսած, երբ շատ կոլիզիաներ են)։
- Երբ ավելացվում է տարր (
put):- Հաշվարկվում է բանալի հեշ կոդը (
key.hashCode())։ - Հեշ կոդը փոփոխվում է ավելի լավ բաշխման համար (
hash)։ - Օգտագործելով փոփոխված հեշը և զանգվածի չափը, հաշվարկվում է բաքի ինդեքսը (
hash & (array_size - 1))։ - Տարրը ("բանալի-արժեք" զույգը որպես
Nodeօբյեկտ) տեղադրվում է այդ բաքում։ Եթե բաքը արդեն պարունակում է տարրեր, նոր տարրը ավելացվում է կապակցված ցանկի կամ ծառի սկզբում։ - Ավելացման ժամանակ ստուգվում է բանալիի առկայությունը։ Օգտագործվում է
equals()մեթոդը բանալիների համեմատության համար։ Եթե բանալին գտնվի, արժեքը թարմացվում է։
- Հաշվարկվում է բանալի հեշ կոդը (
- Երբ ստացվում է տարր (
get):- Հաշվարկվում է բաքի ինդեքսը բանալիով։
- Տարրն որոնվում է բաքի ներսում
hashCode()ևequals()մեթոդների միջոցով։ - Վերադարձվում է կապված արժեքը։
Կազմավորում:
- Կոլիզիաներ: Եթե մի քանի բանալի ունեն նույն հեշ կոդը և ընկնում են նույն բաքում, տարրերը պահվում են կապակցված ցանկի տեսքով։ Java 8-ից հետո, եթե բաքում տարրերի թիվը գերազանցում է սահմանը (հաճախ 8), կապակցված ցանկը փոխարկվում է ծառի՝ ավելի արագ որոնման համար (O(log n) փոխարենը O(n))։
- Չափի փոփոխություն: Երբ տարրերի թիվը գերազանցում է «բեռնման շերտը» (
load factor * capacity), HashMap-ը մեծացնում է ներքին զանգվածի չափը (հաճախ կրկնապատկում) և նորից հեշավորում բոլոր տարրերը։ Սա թանկ գործողություն է (O(n))։ - Պարամետրեր:
capacity: սկզբնական զանգվածի չափը (առաջնական 16)։load factor: բեռնման շերտը (առաջնական 0.75)։ Կարգավորում է, երբ պետք է չափը փոխել։
Ինչու hashCode() և equals()-ը կարևոր են:
- HashMap-ի ճիշտ աշխատանքը կախված է այս մեթոդների ճիշտ իրականացմանից։
- Եթե
equals()երկու օբյեկտների համար վերադարձնում էtrue, ապաhashCode()-ը պետք է վերադարձնի նույն արժեքը։ - Անճիշտ իրականացումը կարող է հանգեցնել այն բանի, որ տարրերը չեն գտնվի (
getկվերադարձնիnull), նույնիսկ եթե դրանք ավելացվել են։
Node կառուցվածքի օրինակ:
// Ներքին օգտագործման համար
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
HashMap ապահովում է արագ (միջինում O(1)) ավելացում, ստացում և հեռացում տարրերի՝ հավասարաչափ բաշխման դեպքում։ Խիստ կոլիզիաների դեպքում, գործողությունը կարող է դառնալ O(n) կամ O(log n) ծառերով։