diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java index 1b0792b8a738..5c6f06e40b61 100644 --- a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java +++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMap.java @@ -11,6 +11,7 @@ @SuppressWarnings("rawtypes") public class HashMap { private final int hashSize; + private int currentSize; // Correctly track the total number of key-value pairs private final LinkedList[] buckets; /** @@ -21,6 +22,7 @@ public class HashMap { @SuppressWarnings("unchecked") public HashMap(int hashSize) { this.hashSize = hashSize; + this.currentSize = 0; // Safe to suppress warning because we are creating an array of generic type this.buckets = new LinkedList[hashSize]; for (int i = 0; i < hashSize; i++) { @@ -37,9 +39,10 @@ public HashMap(int hashSize) { */ private int computeHash(K key) { if (key == null) { - return 0; // Use a special bucket (e.g., bucket 0) for null keys + return 0; // Use bucket 0 for null keys } int hash = key.hashCode() % hashSize; + // Ensure the hash index is non-negative return hash < 0 ? hash + hashSize : hash; } @@ -52,7 +55,12 @@ private int computeHash(K key) { */ public void insert(K key, V value) { int hash = computeHash(key); - buckets[hash].insert(key, value); + boolean keyExisted = buckets[hash].insert(key, value); + + // Only increment size if a new key was inserted (not an update) + if (!keyExisted) { + currentSize++; + } } /** @@ -62,7 +70,12 @@ public void insert(K key, V value) { */ public void delete(K key) { int hash = computeHash(key); - buckets[hash].delete(key); + boolean deletedSuccessfully = buckets[hash].delete(key); + + // Only decrement size if deletion was successful + if (deletedSuccessfully) { + currentSize--; + } } /** @@ -73,8 +86,8 @@ public void delete(K key) { */ public V search(K key) { int hash = computeHash(key); - Node node = buckets[hash].findKey(key); - return node != null ? node.getValue() : null; + // Changed LinkedList to return the value directly for better encapsulation + return buckets[hash].findValue(key); } /** @@ -87,80 +100,71 @@ public void display() { } /** - * Clears the contents of the hash map by reinitializing each bucket. + * Clears the contents of the hash map by reinitializing each bucket and resetting the size. */ public void clear() { for (int i = 0; i < hashSize; i++) { buckets[i] = new LinkedList<>(); } + currentSize = 0; // Reset size } /** * Gets the number of key-value pairs in the hash map. + * Corrected to return the stored 'currentSize' in O(1) time. * * @return the number of key-value pairs in the hash map */ public int size() { - int size = 0; - for (int i = 0; i < hashSize; i++) { - size += buckets[i].isEmpty() ? 0 : 1; - } - return size; + return currentSize; } + // --- NESTED LINKED LIST CLASS (Collision Handler) --- + /** * A nested static class that represents a linked list used for separate chaining in the hash map. - * - * @param the type of keys maintained by this linked list - * @param the type of mapped values */ public static class LinkedList { private Node head; /** * Inserts the specified key-value pair into the linked list. - * If the linked list is empty, the pair becomes the head. - * Otherwise, the pair is added to the end of the list. + * If the key already exists, the value is updated. * * @param key the key to be inserted * @param value the value to be associated with the key + * @return true if an existing key was updated, false if a new node was created. */ - public void insert(K key, V value) { - Node existingNode = findKey(key); + public boolean insert(K key, V value) { + Node existingNode = findNode(key); if (existingNode != null) { - existingNode.setValue(value); // Update the value, even if it's null + existingNode.setValue(value); + return true; // Key existed, value updated } else { + Node newNode = new Node<>(key, value); if (isEmpty()) { - head = new Node<>(key, value); + head = newNode; } else { - Node temp = findEnd(head); - temp.setNext(new Node<>(key, value)); + Node temp = head; // Start search from head + while (temp.getNext() != null) { + temp = temp.getNext(); + } + temp.setNext(newNode); } + return false; // New key added } } /** - * Finds the last node in the linked list. - * - * @param node the starting node - * @return the last node in the linked list - */ - private Node findEnd(Node node) { - while (node.getNext() != null) { - node = node.getNext(); - } - return node; - } - - /** - * Finds the node associated with the specified key in the linked list. + * Finds the node associated with the specified key. * * @param key the key to search for * @return the node associated with the specified key, or null if not found */ - public Node findKey(K key) { + private Node findNode(K key) { Node temp = head; while (temp != null) { + // Robust key comparison, handles null keys correctly if ((key == null && temp.getKey() == null) || (temp.getKey() != null && temp.getKey().equals(key))) { return temp; } @@ -169,32 +173,46 @@ public Node findKey(K key) { return null; } + /** + * Finds the value associated with the specified key. + * + * @param key the key to search for + * @return the value associated with the specified key, or null if not found + */ + public V findValue(K key) { + Node node = findNode(key); + return node != null ? node.getValue() : null; + } + + /** * Deletes the node associated with the specified key from the linked list. - * Handles the case where the key could be null. * * @param key the key whose associated node is to be deleted + * @return true if the node was successfully deleted, false otherwise. */ - public void delete(K key) { + public boolean delete(K key) { if (isEmpty()) { - return; + return false; } - // Handle the case where the head node has the key to delete + // Handle head deletion if ((key == null && head.getKey() == null) || (head.getKey() != null && head.getKey().equals(key))) { head = head.getNext(); - return; + return true; } - // Traverse the list to find and delete the node + // Traverse to find node to delete Node current = head; while (current.getNext() != null) { - if ((key == null && current.getNext().getKey() == null) || (current.getNext().getKey() != null && current.getNext().getKey().equals(key))) { - current.setNext(current.getNext().getNext()); - return; + Node nextNode = current.getNext(); + if ((key == null && nextNode.getKey() == null) || (nextNode.getKey() != null && nextNode.getKey().equals(key))) { + current.setNext(nextNode.getNext()); + return true; } current = current.getNext(); } + return false; // Key not found } /** @@ -203,17 +221,8 @@ public void delete(K key) { * @return a string representation of the linked list */ public String display() { - return display(head); - } - - /** - * Constructs a string representation of the linked list non-recursively. - * - * @param node the starting node - * @return a string representation of the linked list starting from the given node - */ - private String display(Node node) { StringBuilder sb = new StringBuilder(); + Node node = head; while (node != null) { sb.append(node.getKey()).append("=").append(node.getValue()); node = node.getNext(); @@ -221,7 +230,7 @@ private String display(Node node) { sb.append(" -> "); } } - return sb.toString().isEmpty() ? "null" : sb.toString(); + return sb.length() > 0 ? sb.toString() : "empty"; } /** @@ -234,64 +243,37 @@ public boolean isEmpty() { } } + // --- NESTED NODE CLASS (Key-Value Pair) --- + /** - * A nested static class representing a node in the linked list. - * - * @param the type of key maintained by this node - * @param the type of value maintained by this node + * A nested static class representing a node (Entry) in the linked list. */ public static class Node { private final K key; private V value; private Node next; - /** - * Constructs a Node with the specified key and value. - * - * @param key the key associated with this node - * @param value the value associated with this node - */ public Node(K key, V value) { this.key = key; this.value = value; } - /** - * Gets the key associated with this node. - * - * @return the key associated with this node - */ public K getKey() { return key; } - /** - * Gets the value associated with this node. - * - * @return the value associated with this node - */ public V getValue() { return value; } - public void setValue(V value) { // This method allows updating the value + public void setValue(V value) { this.value = value; } - /** - * Gets the next node in the linked list. - * - * @return the next node in the linked list - */ public Node getNext() { return next; } - /** - * Sets the next node in the linked list. - * - * @param next the next node to be linked - */ public void setNext(Node next) { this.next = next; }