diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java
index 416cee99d028..3637e323f097 100644
--- a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java
+++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArray.java
@@ -2,23 +2,45 @@
import java.util.LinkedList;
-// implementation of generic hashmaps using array of Linked Lists
-
+/**
+ * A generic implementation of a hash map using an array of linked lists for collision resolution.
+ * This class provides a way to store key-value pairs efficiently, allowing for average-case
+ * constant time complexity for insertion, deletion, and retrieval operations.
+ *
+ *
+ * The hash map uses separate chaining for collision resolution. Each bucket in the hash map is a
+ * linked list that stores nodes containing key-value pairs. When a collision occurs (i.e., when
+ * two keys hash to the same index), the new key-value pair is simply added to the corresponding
+ * linked list.
+ *
+ *
+ *
+ * The hash map automatically resizes itself when the load factor exceeds 0.75. The load factor is
+ * defined as the ratio of the number of entries to the number of buckets. When resizing occurs,
+ * all existing entries are rehashed and inserted into the new buckets.
+ *
+ *
+ * @param the type of keys maintained by this hash map
+ * @param the type of mapped values
+ */
public class GenericHashMapUsingArray {
- private int size; // n (total number of key-value pairs)
- private LinkedList[] buckets; // N = buckets.length
- private float lf = 0.75f;
+ private int size; // Total number of key-value pairs
+ private LinkedList[] buckets; // Array of linked lists (buckets) for storing entries
+ /**
+ * Constructs a new empty hash map with an initial capacity of 16.
+ */
public GenericHashMapUsingArray() {
initBuckets(16);
size = 0;
}
- // load factor = 0.75 means if we need to add 100 items and we have added
- // 75, then adding 76th item it will double the size, copy all elements
- // & then add 76th item.
-
+ /**
+ * Initializes the buckets for the hash map with the specified number of buckets.
+ *
+ * @param n the number of buckets to initialize
+ */
private void initBuckets(int n) {
buckets = new LinkedList[n];
for (int i = 0; i < buckets.length; i++) {
@@ -26,43 +48,66 @@ private void initBuckets(int n) {
}
}
+ /**
+ * Associates the specified value with the specified key in this map.
+ * If the map previously contained a mapping for the key, the old value is replaced.
+ *
+ * @param key the key with which the specified value is to be associated
+ * @param value the value to be associated with the specified key
+ */
public void put(K key, V value) {
int bucketIndex = hashFunction(key);
LinkedList nodes = buckets[bucketIndex];
- for (Node node : nodes) { // if key present => update
+ // Update existing key's value if present
+ for (Node node : nodes) {
if (node.key.equals(key)) {
node.value = value;
return;
}
}
- // key is not present => insert
+ // Insert new key-value pair
nodes.add(new Node(key, value));
size++;
- if ((float) size / buckets.length > lf) {
+ // Check if rehashing is needed
+ // Load factor threshold for resizing
+ float loadFactorThreshold = 0.75f;
+ if ((float) size / buckets.length > loadFactorThreshold) {
reHash();
}
}
- // tells which bucket to go to
+ /**
+ * Returns the index of the bucket in which the key would be stored.
+ *
+ * @param key the key whose bucket index is to be computed
+ * @return the bucket index
+ */
private int hashFunction(K key) {
return Math.floorMod(key.hashCode(), buckets.length);
}
+ /**
+ * Rehashes the map by doubling the number of buckets and re-inserting all entries.
+ */
private void reHash() {
- System.out.println("Rehashing!");
- LinkedList[] old = buckets;
- initBuckets(old.length * 2);
+ LinkedList[] oldBuckets = buckets;
+ initBuckets(oldBuckets.length * 2);
this.size = 0;
- for (LinkedList nodes : old) {
+ for (LinkedList nodes : oldBuckets) {
for (Node node : nodes) {
put(node.key, node.value);
}
}
}
+ /**
+ * Removes the mapping for the specified key from this map if present.
+ *
+ * @param key the key whose mapping is to be removed from the map
+ */
public void remove(K key) {
int bucketIndex = hashFunction(key);
LinkedList nodes = buckets[bucketIndex];
@@ -74,14 +119,28 @@ public void remove(K key) {
break;
}
}
- nodes.remove(target);
- size--;
+
+ if (target != null) {
+ nodes.remove(target);
+ size--;
+ }
}
+ /**
+ * Returns the number of key-value pairs in this map.
+ *
+ * @return the number of key-value pairs
+ */
public int size() {
return this.size;
}
+ /**
+ * Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
+ *
+ * @param key the key whose associated value is to be returned
+ * @return the value associated with the specified key, or null if no mapping exists
+ */
public V get(K key) {
int bucketIndex = hashFunction(key);
LinkedList nodes = buckets[bucketIndex];
@@ -96,7 +155,6 @@ public V get(K key) {
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
-
builder.append("{");
for (LinkedList nodes : buckets) {
for (Node node : nodes) {
@@ -106,19 +164,37 @@ public String toString() {
builder.append(", ");
}
}
+ // Remove trailing comma and space
+ if (builder.length() > 1) {
+ builder.setLength(builder.length() - 2);
+ }
builder.append("}");
return builder.toString();
}
+ /**
+ * Returns true if this map contains a mapping for the specified key.
+ *
+ * @param key the key whose presence in this map is to be tested
+ * @return true if this map contains a mapping for the specified key
+ */
public boolean containsKey(K key) {
return get(key) != null;
}
+ /**
+ * A private class representing a key-value pair (node) in the hash map.
+ */
public class Node {
-
K key;
V value;
+ /**
+ * Constructs a new Node with the specified key and value.
+ *
+ * @param key the key of the key-value pair
+ * @param value the value of the key-value pair
+ */
public Node(K key, V value) {
this.key = key;
this.value = value;
diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java
index 483e43bb5cb3..5d1733a3e97c 100644
--- a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java
+++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java
@@ -50,4 +50,47 @@ void testGenericHashmapWhichUsesArrayAndKeyIsIntegerValueIsString() {
assertEquals("Washington DC", map.get(101));
assertTrue(map.containsKey(46));
}
+
+ @Test
+ void testRemoveNonExistentKey() {
+ GenericHashMapUsingArray map = new GenericHashMapUsingArray<>();
+ map.put("USA", "Washington DC");
+ map.remove("Nepal"); // Attempting to remove a non-existent key
+ assertEquals(1, map.size()); // Size should remain the same
+ }
+
+ @Test
+ void testRehashing() {
+ GenericHashMapUsingArray map = new GenericHashMapUsingArray<>();
+ for (int i = 0; i < 20; i++) {
+ map.put("Key" + i, "Value" + i);
+ }
+ assertEquals(20, map.size()); // Ensure all items were added
+ assertEquals("Value5", map.get("Key5")); // Check retrieval after rehash
+ }
+
+ @Test
+ void testUpdateValueForExistingKey() {
+ GenericHashMapUsingArray map = new GenericHashMapUsingArray<>();
+ map.put("USA", "Washington DC");
+ map.put("USA", "New Washington DC"); // Updating value for existing key
+ assertEquals("New Washington DC", map.get("USA"));
+ }
+
+ @Test
+ void testToStringMethod() {
+ GenericHashMapUsingArray map = new GenericHashMapUsingArray<>();
+ map.put("USA", "Washington DC");
+ map.put("Nepal", "Kathmandu");
+ String expected = "{USA : Washington DC, Nepal : Kathmandu}";
+ assertEquals(expected, map.toString());
+ }
+
+ @Test
+ void testContainsKey() {
+ GenericHashMapUsingArray map = new GenericHashMapUsingArray<>();
+ map.put("USA", "Washington DC");
+ assertTrue(map.containsKey("USA"));
+ assertFalse(map.containsKey("Nepal"));
+ }
}