diff --git a/DIRECTORY.md b/DIRECTORY.md
index 1c1c010221a3..284c396b2796 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -857,12 +857,12 @@
* hashing
* [GenericHashMapUsingArrayListTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayListTest.java)
* [GenericHashMapUsingArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayTest.java)
+ * [HashMapCuckooHashingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java)
* [HashMapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapTest.java)
* [IntersectionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/IntersectionTest.java)
* [LinearProbingHashMapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/LinearProbingHashMapTest.java)
* [MajorityElementTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MajorityElementTest.java)
* [MapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/MapTest.java)
- * [HashMapCuckooHashingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/hashmap/HashMapCuckooHashingTest.java)
* heaps
* [FibonacciHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/FibonacciHeapTest.java)
* [GenericHeapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/heaps/GenericHeapTest.java)
diff --git a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java
index a67968d7e659..8bcf00730acb 100644
--- a/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java
+++ b/src/main/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashing.java
@@ -3,25 +3,26 @@
import java.util.Objects;
/**
- * This class is an implementation of a hash table using Cuckoo Hashing It uses
- * a dynamic array to lengthen the size of the hash table when load factor > .7
+ * This class implements a hash table using Cuckoo Hashing.
+ * Cuckoo hashing is a type of open-addressing hash table that resolves collisions
+ * by relocating existing keys. It utilizes two hash functions to minimize collisions
+ * and automatically resizes the table when the load factor exceeds 0.7.
*
- * ...
+ * For more information on cuckoo hashing, refer to
+ * this Wikipedia page.
*/
public class HashMapCuckooHashing {
- private int tableSize; // size of the hash table
- private Integer[] buckets; // array representing the table
- private final Integer emptySlot;
- private int size; // number of elements in the hash table
-
- private int thresh; // threshold for infinite loop checking
+ private int tableSize; // Size of the hash table
+ private Integer[] buckets; // Array representing the hash table
+ private final Integer emptySlot; // Placeholder for deleted slots
+ private int size; // Number of elements in the hash table
+ private int thresh; // Threshold for detecting infinite loops during insertion
/**
- * Constructor initializes buckets array, hsize, and creates dummy object
- * for emptySlot
+ * Constructs a HashMapCuckooHashing object with the specified initial table size.
*
- * @param tableSize the desired size of the hash map
+ * @param tableSize the initial size of the hash map
*/
public HashMapCuckooHashing(int tableSize) {
this.buckets = new Integer[tableSize];
@@ -32,13 +33,11 @@ public HashMapCuckooHashing(int tableSize) {
}
/**
- * The 2 Hash Functions takes a given key and finds an index based on its data, 2 distinctive
- * ways to minimize collisions
+ * Computes the first hash index for a given key using the modulo operation.
*
- * @param key the desired key to be converted
- * @return int an index corresponding to the key
+ * @param key the key for which the hash index is computed
+ * @return an integer index corresponding to the key
*/
-
public int hashFunction1(int key) {
int hash = key % tableSize;
if (hash < 0) {
@@ -47,6 +46,12 @@ public int hashFunction1(int key) {
return hash;
}
+ /**
+ * Computes the second hash index for a given key using integer division.
+ *
+ * @param key the key for which the hash index is computed
+ * @return an integer index corresponding to the key
+ */
public int hashFunction2(int key) {
int hash = key / tableSize;
hash %= tableSize;
@@ -57,14 +62,14 @@ public int hashFunction2(int key) {
}
/**
- * inserts the key into the hash map by wrapping it as an Integer object, then uses while loop
- * to insert new key if desired place is empty, return. if already occupied, continue while loop
- * over the new key that has just been pushed out. if while loop continues more than Thresh,
- * rehash table to new size, then push again.
+ * Inserts a key into the hash table using cuckoo hashing.
+ * If the target bucket is occupied, it relocates the existing key and attempts to insert
+ * it into its alternate location. If the insertion process exceeds the threshold,
+ * the table is resized.
*
- * @param key the desired key to be inserted in the hash map
+ * @param key the key to be inserted into the hash table
+ * @throws IllegalArgumentException if the key already exists in the table
*/
-
public void insertKey2HashTable(int key) {
Integer wrappedInt = key;
Integer temp;
@@ -77,7 +82,7 @@ public void insertKey2HashTable(int key) {
}
if (checkTableContainsKey(key)) {
- throw new IllegalArgumentException("Key already inside, no duplicates allowed");
+ throw new IllegalArgumentException("Key already exists; duplicates are not allowed.");
}
while (loopCounter <= thresh) {
@@ -117,9 +122,7 @@ public void insertKey2HashTable(int key) {
}
/**
- * creates new HashMapCuckooHashing object, then inserts each of the elements in the previous
- * table to it with its new hash functions. then refers current array to new table.
- *
+ * Rehashes the current table to a new size (double the current size) and reinserts existing keys.
*/
public void reHashTableIncreasesTableSize() {
HashMapCuckooHashing newT = new HashMapCuckooHashing(tableSize * 2);
@@ -134,15 +137,16 @@ public void reHashTableIncreasesTableSize() {
}
/**
- * deletes a key from the hash map and adds an available placeholder
+ * Deletes a key from the hash table, marking its position as available.
*
- * @param key the desired key to be deleted
+ * @param key the key to be deleted from the hash table
+ * @throws IllegalArgumentException if the table is empty or if the key is not found
*/
public void deleteKeyFromHashTable(int key) {
Integer wrappedInt = key;
int hash = hashFunction1(key);
if (isEmpty()) {
- throw new IllegalArgumentException("Table is empty");
+ throw new IllegalArgumentException("Table is empty, cannot delete.");
}
if (Objects.equals(buckets[hash], wrappedInt)) {
@@ -157,11 +161,11 @@ public void deleteKeyFromHashTable(int key) {
size--;
return;
}
- throw new IllegalArgumentException("Key " + key + " already inside, no duplicates allowed");
+ throw new IllegalArgumentException("Key " + key + " not found in the table.");
}
/**
- * Displays the hash table line by line
+ * Displays the hash table contents, bucket by bucket.
*/
public void displayHashtable() {
for (int i = 0; i < tableSize; i++) {
@@ -175,17 +179,18 @@ public void displayHashtable() {
}
/**
- * Finds the index of location based on an inputted key
+ * Finds the index of a given key in the hash table.
*
- * @param key the desired key to be found
- * @return int the index where the key is located
+ * @param key the key to be found
+ * @return the index where the key is located
+ * @throws IllegalArgumentException if the table is empty or the key is not found
*/
public int findKeyInTable(int key) {
Integer wrappedInt = key;
int hash = hashFunction1(key);
if (isEmpty()) {
- throw new IllegalArgumentException("Table is empty");
+ throw new IllegalArgumentException("Table is empty; cannot find keys.");
}
if (Objects.equals(buckets[hash], wrappedInt)) {
@@ -194,66 +199,70 @@ public int findKeyInTable(int key) {
hash = hashFunction2(key);
if (!Objects.equals(buckets[hash], wrappedInt)) {
- throw new IllegalArgumentException("Key " + key + " not found in table");
+ throw new IllegalArgumentException("Key " + key + " not found in the table.");
} else {
return hash;
}
}
/**
- * checks if key is inside without any output other than returned boolean.
+ * Checks if the given key is present in the hash table.
*
- * @param key the desired key to be found
- * @return int the index where the key is located
+ * @param key the key to be checked
+ * @return true if the key exists, false otherwise
*/
public boolean checkTableContainsKey(int key) {
- return ((buckets[hashFunction1(key)] != null && buckets[hashFunction1(key)].equals(key)) || (buckets[hashFunction2(key)] != null && buckets[hashFunction2(key)] == key));
+ return ((buckets[hashFunction1(key)] != null && buckets[hashFunction1(key)].equals(key)) || (buckets[hashFunction2(key)] != null && buckets[hashFunction2(key)].equals(key)));
}
/**
- * Checks the load factor of the hash table if greater than .7,
- * automatically lengthens table to prevent further collisions
+ * Checks the load factor of the hash table. If the load factor exceeds 0.7,
+ * the table is resized to prevent further collisions.
+ *
+ * @return the current load factor of the hash table
*/
public double checkLoadFactor() {
double factor = (double) size / tableSize;
if (factor > .7) {
- System.out.printf("Load factor is %.2f , rehashing table%n", factor);
+ System.out.printf("Load factor is %.2f, rehashing table.%n", factor);
reHashTableIncreasesTableSize();
}
return factor;
}
/**
- * isFull returns true if the hash map is full and false if not full
+ * Checks if the hash map is full.
*
- * @return boolean is Empty
+ * @return true if the hash map is full, false otherwise
*/
public boolean isFull() {
- boolean response = true;
for (int i = 0; i < tableSize; i++) {
if (buckets[i] == null || Objects.equals(buckets[i], emptySlot)) {
return false;
}
}
- return response;
+ return true;
}
/**
- * isEmpty returns true if the hash map is empty and false if not empty
+ * Checks if the hash map is empty.
*
- * @return boolean is Empty
+ * @return true if the hash map is empty, false otherwise
*/
public boolean isEmpty() {
- boolean response = true;
for (int i = 0; i < tableSize; i++) {
if (buckets[i] != null) {
- response = false;
- break;
+ return false;
}
}
- return response;
+ return true;
}
+ /**
+ * Returns the current number of keys in the hash table.
+ *
+ * @return the number of keys present in the hash table
+ */
public int getNumberOfKeysInTable() {
return size;
}
diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/HashMapCuckooHashingTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/HashMapCuckooHashingTest.java
deleted file mode 100644
index 14bddeae1c91..000000000000
--- a/src/test/java/com/thealgorithms/datastructures/hashmap/HashMapCuckooHashingTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.thealgorithms.datastructures.hashmap;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-import com.thealgorithms.datastructures.hashmap.hashing.HashMapCuckooHashing;
-import org.junit.jupiter.api.Test;
-
-class HashMapCuckooHashingTest {
-
- @Test
- void insertKey() {
- HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
- assertEquals(0, hashTable.getNumberOfKeysInTable());
-
- hashTable.insertKey2HashTable(3);
-
- assertEquals(1, hashTable.getNumberOfKeysInTable());
- }
-
- @Test
- void getKeyIndex() {
- HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
- hashTable.insertKey2HashTable(8);
- hashTable.insertKey2HashTable(4);
-
- assertNotEquals(-1, hashTable.findKeyInTable(8));
- }
-
- @Test
- void containsKey() {
- HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
- hashTable.insertKey2HashTable(8);
- boolean contains = hashTable.checkTableContainsKey(8);
-
- assertTrue(contains);
- }
-
- @Test
- void removeKey() {
- HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
- hashTable.insertKey2HashTable(3);
-
- int initialSize = hashTable.getNumberOfKeysInTable();
-
- hashTable.deleteKeyFromHashTable(3);
-
- assertEquals(initialSize - 1, hashTable.getNumberOfKeysInTable());
- }
-
- @Test
- void removeNone() {
- HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
- try {
- hashTable.deleteKeyFromHashTable(3);
- } catch (Exception e) {
- assertTrue(true);
- return;
- }
- fail();
- }
-
- @Test
- void reHashTableIncreasesTableSize() {
- HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
- int initialSize = hashTable.getNumberOfKeysInTable();
-
- hashTable.reHashTableIncreasesTableSize();
-
- assertEquals(initialSize * 2, hashTable.getNumberOfKeysInTable());
- }
-
- @Test
- void hashFunctionsAreDifferent() {
- HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
- hashTable.insertKey2HashTable(33);
-
- assertNotEquals(hashTable.hashFunction1(3), hashTable.hashFunction2(3));
- }
-
- @Test
- void avoidInfiniteLoops() {
- HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
- hashTable.insertKey2HashTable(0);
- hashTable.insertKey2HashTable(10);
- hashTable.insertKey2HashTable(100);
-
- assertTrue(hashTable.checkTableContainsKey(0));
- assertTrue(hashTable.checkTableContainsKey(10));
- assertTrue(hashTable.checkTableContainsKey(100));
- }
-}
diff --git a/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java
new file mode 100644
index 000000000000..c2f80bfe3210
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/hashmap/hashing/HashMapCuckooHashingTest.java
@@ -0,0 +1,140 @@
+package com.thealgorithms.datastructures.hashmap.hashing;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class HashMapCuckooHashingTest {
+
+ @Test
+ void insertKey() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ assertEquals(0, hashTable.getNumberOfKeysInTable());
+
+ hashTable.insertKey2HashTable(3);
+ assertEquals(1, hashTable.getNumberOfKeysInTable());
+ }
+
+ @Test
+ void getKeyIndex() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ hashTable.insertKey2HashTable(8);
+ hashTable.insertKey2HashTable(4);
+
+ assertNotEquals(-1, hashTable.findKeyInTable(8));
+ }
+
+ @Test
+ void containsKey() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ hashTable.insertKey2HashTable(8);
+ boolean contains = hashTable.checkTableContainsKey(8);
+
+ assertTrue(contains);
+ }
+
+ @Test
+ void removeKey() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ hashTable.insertKey2HashTable(3);
+
+ int initialSize = hashTable.getNumberOfKeysInTable();
+ hashTable.deleteKeyFromHashTable(3);
+
+ assertEquals(initialSize - 1, hashTable.getNumberOfKeysInTable());
+ }
+
+ @Test
+ void removeNone() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ try {
+ hashTable.deleteKeyFromHashTable(3);
+ } catch (Exception e) {
+ assertTrue(true);
+ return;
+ }
+ Assertions.fail("Expected exception when trying to delete a non-existing key.");
+ }
+
+ @Test
+ void reHashTableIncreasesTableSize() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ hashTable.insertKey2HashTable(1);
+ hashTable.insertKey2HashTable(2);
+ hashTable.insertKey2HashTable(3);
+ hashTable.insertKey2HashTable(4);
+ hashTable.insertKey2HashTable(5);
+ hashTable.insertKey2HashTable(6);
+ hashTable.insertKey2HashTable(7);
+ hashTable.insertKey2HashTable(8);
+ hashTable.insertKey2HashTable(9);
+ hashTable.insertKey2HashTable(10); // This should trigger rehashing
+
+ assertEquals(10, hashTable.getNumberOfKeysInTable());
+ }
+
+ @Test
+ void hashFunctionsAreDifferent() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ hashTable.insertKey2HashTable(33);
+
+ assertNotEquals(hashTable.hashFunction1(3), hashTable.hashFunction2(3), "Hash functions should produce different indices.");
+ }
+
+ @Test
+ void avoidInfiniteLoops() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ hashTable.insertKey2HashTable(0);
+ hashTable.insertKey2HashTable(10);
+ hashTable.insertKey2HashTable(100);
+
+ assertTrue(hashTable.checkTableContainsKey(0));
+ assertTrue(hashTable.checkTableContainsKey(10));
+ assertTrue(hashTable.checkTableContainsKey(100));
+ }
+
+ @Test
+ void testLoadFactor() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ for (int i = 1; i <= 8; i++) { // Insert 8 keys to test load factor
+ hashTable.insertKey2HashTable(i);
+ }
+ assertEquals(8, hashTable.getNumberOfKeysInTable());
+ // Check that rehashing occurs when a 9th key is added
+ hashTable.insertKey2HashTable(9);
+ assertTrue(hashTable.getNumberOfKeysInTable() > 8, "Load factor exceeded, table should have been resized.");
+ }
+
+ @Test
+ void testDeleteNonExistentKey() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ hashTable.insertKey2HashTable(1);
+ hashTable.insertKey2HashTable(2);
+
+ Exception exception = null;
+ try {
+ hashTable.deleteKeyFromHashTable(3); // Try deleting a non-existent key
+ } catch (IllegalArgumentException e) {
+ exception = e; // Capture the exception
+ }
+ assertNotNull(exception, "Expected an IllegalArgumentException when deleting a non-existent key.");
+ }
+
+ @Test
+ void testInsertDuplicateKey() {
+ HashMapCuckooHashing hashTable = new HashMapCuckooHashing(10);
+ hashTable.insertKey2HashTable(1);
+ Exception exception = null;
+
+ try {
+ hashTable.insertKey2HashTable(1); // Attempt to insert duplicate key
+ } catch (IllegalArgumentException e) {
+ exception = e; // Capture the exception
+ }
+ assertNotNull(exception, "Expected an IllegalArgumentException for duplicate key insertion.");
+ }
+}