Middle
How is the HashMap data structure organized and structured in Java?
sobes.tech AI
Answer from AI
HashMap in Java is based on hashing principles. It stores key-value pairs.
Internal structure:
- Array of buckets (containers). Each bucket is a linked list (or a tree, starting from Java 8, when there are many collisions).
- When adding an element (
put):- The hash code of the key (
key.hashCode()) is computed. - The hash code is modified for better distribution (
hash). - Using the modified hash and the size of the bucket array, the index of the bucket where the element will be placed is calculated (
hash & (array_size - 1)). - The element (a "key-value" pair as a
Nodeobject) is placed into this bucket. If the bucket already contains elements, the new element is added at the beginning of the linked list or tree. - When adding, it checks for the presence of the key: the
equals()method is used to compare keys inside the bucket. If the key is found, the value is updated.
- The hash code of the key (
- When retrieving an element (
get):- The bucket index is calculated by the key.
- Inside the bucket, the element is searched by key using
hashCode()andequals()methods. - The associated value is returned.
Organization:
- Collisions: If multiple keys have the same hash code and fall into the same bucket, the elements are stored as a linked list. From Java 8, when the number of elements in a bucket exceeds a threshold (usually 8), the linked list is converted into a tree for faster search (
O(log n)instead ofO(n)). - Resizing: When the number of elements exceeds the "load factor" (
load factor * capacity), HashMap increases the size of the internal bucket array (usually doubles) and rehashes all elements. This is an expensive operation (O(n)). - Parameters:
capacity: initial size of the bucket array (default 16).load factor: load threshold (default 0.75). It determines when resizing occurs.
Why hashCode() and equals() are important:
- Proper functioning of HashMap depends on correct implementation of these methods.
- If
equals()returnstruefor two objects, thenhashCode()must return the same value. - Incorrect implementation can lead to elements not being found (
getreturnsnull) even if they were added.
Example of Node structure:
// Internal only
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 provides fast (average O(1)) addition, retrieval, and removal of elements with uniform hash distribution. In the worst case (heavy collisions), the operation can become O(n) or O(log n) with trees.
Not thread-safe. For thread-safe use, ConcurrentHashMap or Collections.synchronizedMap(new HashMap<...>(...)) should be used.