Middle
Hogyan van felépítve és szervezve a HashMap adatstruktúra Java-ban?
sobes.tech MI
Válasz az MI-től
A HashMap Java-ban a hashelés elvein alapul. Párokat tárol "kulcs-érték" formában.
Belső felépítés:
- Kádak (tárolók) tömbje. Minden kád egy láncolt listát (vagy fát, Java 8-tól kezdve, ha sok ütközés van).
- Elem hozzáadása (
put):- A kulcs hash kódja számítódik (
key.hashCode()). - A hash kód módosul a jobb eloszlás érdekében (
hash). - A módosított hash és a tömb mérete alapján számítódik a kád indexe (
hash & (array_size - 1)). - Az elem ("kulcs-érték" pár
Nodeobjektumként) ebbe a kádba kerül. Ha a kád már tartalmaz elemeket, az új elem a láncolt lista vagy fa elejére kerül. - Hozzáadáskor ellenőrzés történik a kulcs meglétére: az
equals()metódus segítségével keresünk a kádon belül. Ha a kulcs megtalálható, az érték frissül.
- A kulcs hash kódja számítódik (
- Elem lekérése (
get):- A kulcs alapján számítódik a kád indexe.
- A kádon belül keresés történik az
hashCode()ésequals()metódusok segítségével. - Visszaadódik a kapcsolódó érték.
Szervezés:
- Ütközések: Ha több kulcs ugyanazzal a hash kóddal rendelkezik és ugyanabba a kádba kerül, azok láncolt listaként tárolódnak. Java 8-tól, ha a kádon belüli elemek száma meghalad egy küszöböt (általában 8), a láncolt lista fává alakul a gyorsabb keresés érdekében (O(log n) helyett O(n)).
- Méret növelése: Amikor az elemek száma meghaladja a
load factor * capacityértéket, a HashMap megnöveli a belső tömb méretét (általában duplájára) és újra hash-eli az összes elemet. Ez erőforrás-igényes művelet (O(n)). - Paraméterek:
capacity: Kezdeti tömbméret (alapértelmezett 16).load factor: Betöltési küszöb (alapértelmezett 0.75). Meghatározza, mikor történjen a méret növelése.
Miért fontosak a hashCode() és equals():
- A HashMap helyes működése ezeknek a metódusoknak a helyes implementációjától függ.
- Ha az
equals()két objektum eseténtrue-t ad, akkor ahashCode()ugyanazt az értéket kell, hogy adja. - Hibás implementáció esetén az elemek nem találhatók meg (
getnull-t ad), még akkor sem, ha hozzáadtuk őket.
Node szerkezetének példája:
// Csak belső használatra
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;
}
}
A HashMap gyors (átlagosan O(1)) hozzáférést, beszúrást és törlést biztosít, egyenletes eloszlás esetén. Legrosszabb esetben (erős ütközések) az idő O(n) vagy O(log n) lehet fákkal.