Add Immutable HashMap implementation (#7183)

* Add immutable HashMap implementation

* Add immutable HashMap implementation

* Applied clang-format

---------

Co-authored-by: Koushik Sai <nyamathabadkoushik@gmail.com>
This commit is contained in:
NYAMATHABAD KOUSHIK SAI
2025-12-27 22:25:23 +05:30
committed by GitHub
parent 2707da25e3
commit 51335cc18a
2 changed files with 170 additions and 0 deletions

View File

@@ -0,0 +1,115 @@
package com.thealgorithms.datastructures.hashmap.hashing;
/**
* Immutable HashMap implementation using separate chaining.
*
* <p>This HashMap does not allow modification of existing instances.
* Any update operation returns a new ImmutableHashMap.
*
* @param <K> key type
* @param <V> value type
*/
public final class ImmutableHashMap<K, V> {
private static final int DEFAULT_CAPACITY = 16;
private final Node<K, V>[] table;
private final int size;
/**
* Private constructor to enforce immutability.
*/
private ImmutableHashMap(Node<K, V>[] table, int size) {
this.table = table;
this.size = size;
}
/**
* Creates an empty ImmutableHashMap.
*
* @param <K> key type
* @param <V> value type
* @return empty ImmutableHashMap
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public static <K, V> ImmutableHashMap<K, V> empty() {
Node<K, V>[] table = (Node<K, V>[]) new Node[DEFAULT_CAPACITY];
return new ImmutableHashMap<>(table, 0);
}
/**
* Returns a new ImmutableHashMap with the given key-value pair added.
*
* @param key key to add
* @param value value to associate
* @return new ImmutableHashMap instance
*/
public ImmutableHashMap<K, V> put(K key, V value) {
Node<K, V>[] newTable = table.clone();
int index = hash(key);
newTable[index] = new Node<>(key, value, newTable[index]);
return new ImmutableHashMap<>(newTable, size + 1);
}
/**
* Retrieves the value associated with the given key.
*
* @param key key to search
* @return value if found, otherwise null
*/
public V get(K key) {
int index = hash(key);
Node<K, V> current = table[index];
while (current != null) {
if ((key == null && current.key == null) || (key != null && key.equals(current.key))) {
return current.value;
}
current = current.next;
}
return null;
}
/**
* Checks whether the given key exists in the map.
*
* @param key key to check
* @return true if key exists, false otherwise
*/
public boolean containsKey(K key) {
return get(key) != null;
}
/**
* Returns the number of key-value pairs.
*
* @return size of the map
*/
public int size() {
return size;
}
/**
* Computes hash index for a given key.
*/
private int hash(K key) {
return key == null ? 0 : (key.hashCode() & Integer.MAX_VALUE) % table.length;
}
/**
* Node class for separate chaining.
*/
private static final class Node<K, V> {
private final K key;
private final V value;
private final Node<K, V> next;
private Node(K key, V value, Node<K, V> next) {
this.key = key;
this.value = value;
this.next = next;
}
}
}

View File

@@ -0,0 +1,55 @@
package com.thealgorithms.datastructures.hashmap.hashing;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
class ImmutableHashMapTest {
@Test
void testEmptyMap() {
ImmutableHashMap<String, Integer> map = ImmutableHashMap.<String, Integer>empty();
assertEquals(0, map.size());
assertNull(map.get("A"));
}
@Test
void testPutDoesNotModifyOriginalMap() {
ImmutableHashMap<String, Integer> map1 = ImmutableHashMap.<String, Integer>empty();
ImmutableHashMap<String, Integer> map2 = map1.put("A", 1);
assertEquals(0, map1.size());
assertEquals(1, map2.size());
assertNull(map1.get("A"));
assertEquals(1, map2.get("A"));
}
@Test
void testMultiplePuts() {
ImmutableHashMap<String, Integer> map = ImmutableHashMap.<String, Integer>empty().put("A", 1).put("B", 2);
assertEquals(2, map.size());
assertEquals(1, map.get("A"));
assertEquals(2, map.get("B"));
}
@Test
void testContainsKey() {
ImmutableHashMap<String, Integer> map = ImmutableHashMap.<String, Integer>empty().put("X", 100);
assertTrue(map.containsKey("X"));
assertFalse(map.containsKey("Y"));
}
@Test
void testNullKey() {
ImmutableHashMap<String, Integer> map = ImmutableHashMap.<String, Integer>empty().put(null, 50);
assertEquals(50, map.get(null));
}
}