|
1 | 1 | package com.thealgorithms.datastructures.hashmap.hashing;
|
2 | 2 |
|
3 |
| -public class HashMap { |
| 3 | +/** |
| 4 | + * A generic HashMap implementation that uses separate chaining with linked lists |
| 5 | + * to handle collisions. The class supports basic operations such as insert, delete, |
| 6 | + * and search, as well as displaying the contents of the hash map. |
| 7 | + * |
| 8 | + * @param <K> the type of keys maintained by this map |
| 9 | + * @param <V> the type of mapped values |
| 10 | + */ |
| 11 | +public class HashMap<K, V> { |
| 12 | + private final int hashSize; |
| 13 | + private final LinkedList<K, V>[] buckets; |
4 | 14 |
|
5 |
| - private final int hsize; |
6 |
| - private final LinkedList[] buckets; |
7 |
| - |
8 |
| - public HashMap(int hsize) { |
9 |
| - buckets = new LinkedList[hsize]; |
10 |
| - for (int i = 0; i < hsize; i++) { |
11 |
| - buckets[i] = new LinkedList(); |
12 |
| - // Java requires explicit initialization of each object |
| 15 | + /** |
| 16 | + * Constructs a HashMap with the specified hash size. |
| 17 | + * |
| 18 | + * @param hashSize the number of buckets in the hash map |
| 19 | + */ |
| 20 | + @SuppressWarnings("unchecked") |
| 21 | + public HashMap(int hashSize) { |
| 22 | + this.hashSize = hashSize; |
| 23 | + // Safe to suppress warning because we are creating an array of generic type |
| 24 | + this.buckets = new LinkedList[hashSize]; |
| 25 | + for (int i = 0; i < hashSize; i++) { |
| 26 | + buckets[i] = new LinkedList<>(); |
13 | 27 | }
|
14 |
| - this.hsize = hsize; |
15 | 28 | }
|
16 | 29 |
|
17 |
| - public int hashing(int key) { |
18 |
| - int hash = key % hsize; |
19 |
| - if (hash < 0) { |
20 |
| - hash += hsize; |
| 30 | + /** |
| 31 | + * Computes the hash code for the specified key. |
| 32 | + * Null keys are hashed to bucket 0. |
| 33 | + * |
| 34 | + * @param key the key for which the hash code is to be computed |
| 35 | + * @return the hash code corresponding to the key |
| 36 | + */ |
| 37 | + private int computeHash(K key) { |
| 38 | + if (key == null) { |
| 39 | + return 0; // Use a special bucket (e.g., bucket 0) for null keys |
21 | 40 | }
|
22 |
| - return hash; |
| 41 | + int hash = key.hashCode() % hashSize; |
| 42 | + return hash < 0 ? hash + hashSize : hash; |
23 | 43 | }
|
24 | 44 |
|
25 |
| - public void insertHash(int key) { |
26 |
| - int hash = hashing(key); |
27 |
| - buckets[hash].insert(key); |
| 45 | + /** |
| 46 | + * Inserts the specified key-value pair into the hash map. |
| 47 | + * If the key already exists, the value is updated. |
| 48 | + * |
| 49 | + * @param key the key to be inserted |
| 50 | + * @param value the value to be associated with the key |
| 51 | + */ |
| 52 | + public void insert(K key, V value) { |
| 53 | + int hash = computeHash(key); |
| 54 | + buckets[hash].insert(key, value); |
28 | 55 | }
|
29 | 56 |
|
30 |
| - public void deleteHash(int key) { |
31 |
| - int hash = hashing(key); |
32 |
| - |
| 57 | + /** |
| 58 | + * Deletes the key-value pair associated with the specified key from the hash map. |
| 59 | + * |
| 60 | + * @param key the key whose key-value pair is to be deleted |
| 61 | + */ |
| 62 | + public void delete(K key) { |
| 63 | + int hash = computeHash(key); |
33 | 64 | buckets[hash].delete(key);
|
34 | 65 | }
|
35 | 66 |
|
36 |
| - public void displayHashtable() { |
37 |
| - for (int i = 0; i < hsize; i++) { |
38 |
| - System.out.printf("Bucket %d :", i); |
39 |
| - System.out.println(buckets[i].display()); |
40 |
| - } |
| 67 | + /** |
| 68 | + * Searches for the value associated with the specified key in the hash map. |
| 69 | + * |
| 70 | + * @param key the key whose associated value is to be returned |
| 71 | + * @return the value associated with the specified key, or null if the key does not exist |
| 72 | + */ |
| 73 | + public V search(K key) { |
| 74 | + int hash = computeHash(key); |
| 75 | + Node<K, V> node = buckets[hash].findKey(key); |
| 76 | + return node != null ? node.getValue() : null; |
41 | 77 | }
|
42 | 78 |
|
43 |
| - public static class LinkedList { |
44 |
| - |
45 |
| - private Node first; |
46 |
| - |
47 |
| - public LinkedList() { |
48 |
| - first = null; |
| 79 | + /** |
| 80 | + * Displays the contents of the hash map, showing each bucket and its key-value pairs. |
| 81 | + */ |
| 82 | + public void display() { |
| 83 | + for (int i = 0; i < hashSize; i++) { |
| 84 | + System.out.printf("Bucket %d: %s%n", i, buckets[i].display()); |
49 | 85 | }
|
| 86 | + } |
50 | 87 |
|
51 |
| - public void insert(int key) { |
52 |
| - if (isEmpty()) { |
53 |
| - first = new Node(key); |
54 |
| - return; |
55 |
| - } |
| 88 | + /** |
| 89 | + * A nested static class that represents a linked list used for separate chaining in the hash map. |
| 90 | + * |
| 91 | + * @param <K> the type of keys maintained by this linked list |
| 92 | + * @param <V> the type of mapped values |
| 93 | + */ |
| 94 | + public static class LinkedList<K, V> { |
| 95 | + private Node<K, V> head; |
56 | 96 |
|
57 |
| - Node temp = findEnd(first); |
58 |
| - temp.setNext(new Node(key)); |
| 97 | + /** |
| 98 | + * Inserts the specified key-value pair into the linked list. |
| 99 | + * If the linked list is empty, the pair becomes the head. |
| 100 | + * Otherwise, the pair is added to the end of the list. |
| 101 | + * |
| 102 | + * @param key the key to be inserted |
| 103 | + * @param value the value to be associated with the key |
| 104 | + */ |
| 105 | + public void insert(K key, V value) { |
| 106 | + Node<K, V> existingNode = findKey(key); |
| 107 | + if (existingNode != null) { |
| 108 | + existingNode.setValue(value); // Update the value, even if it's null |
| 109 | + } else { |
| 110 | + if (isEmpty()) { |
| 111 | + head = new Node<>(key, value); |
| 112 | + } else { |
| 113 | + Node<K, V> temp = findEnd(head); |
| 114 | + temp.setNext(new Node<>(key, value)); |
| 115 | + } |
| 116 | + } |
59 | 117 | }
|
60 | 118 |
|
61 |
| - private Node findEnd(Node n) { |
62 |
| - while (n.getNext() != null) { |
63 |
| - n = n.getNext(); |
| 119 | + /** |
| 120 | + * Finds the last node in the linked list. |
| 121 | + * |
| 122 | + * @param node the starting node |
| 123 | + * @return the last node in the linked list |
| 124 | + */ |
| 125 | + private Node<K, V> findEnd(Node<K, V> node) { |
| 126 | + while (node.getNext() != null) { |
| 127 | + node = node.getNext(); |
64 | 128 | }
|
65 |
| - return n; |
| 129 | + return node; |
66 | 130 | }
|
67 | 131 |
|
68 |
| - public Node findKey(int key) { |
69 |
| - if (!isEmpty()) { |
70 |
| - Node temp = first; |
71 |
| - if (temp.getKey() == key) { |
| 132 | + /** |
| 133 | + * Finds the node associated with the specified key in the linked list. |
| 134 | + * |
| 135 | + * @param key the key to search for |
| 136 | + * @return the node associated with the specified key, or null if not found |
| 137 | + */ |
| 138 | + public Node<K, V> findKey(K key) { |
| 139 | + Node<K, V> temp = head; |
| 140 | + while (temp != null) { |
| 141 | + if ((key == null && temp.getKey() == null) || (temp.getKey() != null && temp.getKey().equals(key))) { |
72 | 142 | return temp;
|
73 | 143 | }
|
74 |
| - |
75 |
| - while ((temp = temp.getNext()) != null) { |
76 |
| - if (temp.getKey() == key) { |
77 |
| - return temp; |
78 |
| - } |
79 |
| - } |
| 144 | + temp = temp.getNext(); |
80 | 145 | }
|
81 | 146 | return null;
|
82 | 147 | }
|
83 | 148 |
|
84 |
| - public void delete(int key) { |
85 |
| - if (!isEmpty()) { |
86 |
| - if (first.getKey() == key) { |
87 |
| - Node next = first.next; |
88 |
| - first.next = null; // help GC |
89 |
| - first = next; |
90 |
| - } else { |
91 |
| - delete(first, key); |
92 |
| - } |
| 149 | + /** |
| 150 | + * Deletes the node associated with the specified key from the linked list. |
| 151 | + * Handles the case where the key could be null. |
| 152 | + * |
| 153 | + * @param key the key whose associated node is to be deleted |
| 154 | + */ |
| 155 | + public void delete(K key) { |
| 156 | + if (isEmpty()) { |
| 157 | + return; |
93 | 158 | }
|
94 |
| - } |
95 | 159 |
|
96 |
| - private void delete(Node n, int key) { |
97 |
| - if (n.getNext().getKey() == key) { |
98 |
| - if (n.getNext().getNext() == null) { |
99 |
| - n.setNext(null); |
100 |
| - } else { |
101 |
| - n.setNext(n.getNext().getNext()); |
| 160 | + // Handle the case where the head node has the key to delete |
| 161 | + if ((key == null && head.getKey() == null) || (head.getKey() != null && head.getKey().equals(key))) { |
| 162 | + head = head.getNext(); |
| 163 | + return; |
| 164 | + } |
| 165 | + |
| 166 | + // Traverse the list to find and delete the node |
| 167 | + Node<K, V> current = head; |
| 168 | + while (current.getNext() != null) { |
| 169 | + if ((key == null && current.getNext().getKey() == null) || (current.getNext().getKey() != null && current.getNext().getKey().equals(key))) { |
| 170 | + current.setNext(current.getNext().getNext()); |
| 171 | + return; |
102 | 172 | }
|
103 |
| - } else { |
104 |
| - delete(n.getNext(), key); |
| 173 | + current = current.getNext(); |
105 | 174 | }
|
106 | 175 | }
|
107 | 176 |
|
| 177 | + /** |
| 178 | + * Displays the contents of the linked list as a string. |
| 179 | + * |
| 180 | + * @return a string representation of the linked list |
| 181 | + */ |
108 | 182 | public String display() {
|
109 |
| - return display(first); |
| 183 | + return display(head); |
110 | 184 | }
|
111 | 185 |
|
112 |
| - private String display(Node n) { |
113 |
| - if (n == null) { |
114 |
| - return "null"; |
115 |
| - } else { |
116 |
| - return n.getKey() + "->" + display(n.getNext()); |
| 186 | + /** |
| 187 | + * Constructs a string representation of the linked list non-recursively. |
| 188 | + * |
| 189 | + * @param node the starting node |
| 190 | + * @return a string representation of the linked list starting from the given node |
| 191 | + */ |
| 192 | + private String display(Node<K, V> node) { |
| 193 | + StringBuilder sb = new StringBuilder(); |
| 194 | + while (node != null) { |
| 195 | + sb.append(node.getKey()).append("=").append(node.getValue()); |
| 196 | + node = node.getNext(); |
| 197 | + if (node != null) { |
| 198 | + sb.append(" -> "); |
| 199 | + } |
117 | 200 | }
|
| 201 | + return sb.toString().isEmpty() ? "null" : sb.toString(); |
118 | 202 | }
|
119 | 203 |
|
| 204 | + /** |
| 205 | + * Checks if the linked list is empty. |
| 206 | + * |
| 207 | + * @return true if the linked list is empty, false otherwise |
| 208 | + */ |
120 | 209 | public boolean isEmpty() {
|
121 |
| - return first == null; |
| 210 | + return head == null; |
122 | 211 | }
|
123 | 212 | }
|
124 | 213 |
|
125 |
| - public static class Node { |
| 214 | + /** |
| 215 | + * A nested static class representing a node in the linked list. |
| 216 | + * |
| 217 | + * @param <K> the type of key maintained by this node |
| 218 | + * @param <V> the type of value maintained by this node |
| 219 | + */ |
| 220 | + public static class Node<K, V> { |
| 221 | + private final K key; |
| 222 | + private V value; |
| 223 | + private Node<K, V> next; |
126 | 224 |
|
127 |
| - private Node next; |
128 |
| - private final int key; |
129 |
| - |
130 |
| - public Node(int key) { |
131 |
| - next = null; |
| 225 | + /** |
| 226 | + * Constructs a Node with the specified key and value. |
| 227 | + * |
| 228 | + * @param key the key associated with this node |
| 229 | + * @param value the value associated with this node |
| 230 | + */ |
| 231 | + public Node(K key, V value) { |
132 | 232 | this.key = key;
|
| 233 | + this.value = value; |
133 | 234 | }
|
134 | 235 |
|
135 |
| - public Node getNext() { |
136 |
| - return next; |
| 236 | + /** |
| 237 | + * Gets the key associated with this node. |
| 238 | + * |
| 239 | + * @return the key associated with this node |
| 240 | + */ |
| 241 | + public K getKey() { |
| 242 | + return key; |
137 | 243 | }
|
138 | 244 |
|
139 |
| - public int getKey() { |
140 |
| - return key; |
| 245 | + /** |
| 246 | + * Gets the value associated with this node. |
| 247 | + * |
| 248 | + * @return the value associated with this node |
| 249 | + */ |
| 250 | + public V getValue() { |
| 251 | + return value; |
| 252 | + } |
| 253 | + |
| 254 | + public void setValue(V value) { // This method allows updating the value |
| 255 | + this.value = value; |
| 256 | + } |
| 257 | + |
| 258 | + /** |
| 259 | + * Gets the next node in the linked list. |
| 260 | + * |
| 261 | + * @return the next node in the linked list |
| 262 | + */ |
| 263 | + public Node<K, V> getNext() { |
| 264 | + return next; |
141 | 265 | }
|
142 | 266 |
|
143 |
| - public void setNext(Node next) { |
| 267 | + /** |
| 268 | + * Sets the next node in the linked list. |
| 269 | + * |
| 270 | + * @param next the next node to be linked |
| 271 | + */ |
| 272 | + public void setNext(Node<K, V> next) { |
144 | 273 | this.next = next;
|
145 | 274 | }
|
146 | 275 | }
|
|
0 commit comments