mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-06 00:54:32 +08:00
Enhance docs, fix & add tests in `GenericHashMapUsingArrayL… (#5973)
This commit is contained in:
@ -3,12 +3,34 @@ package com.thealgorithms.datastructures.hashmap.hashing;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic implementation of a hash map using an array list of linked lists for collision resolution.
|
||||||
|
* This class allows storage of key-value pairs with average-case constant time complexity for insertion,
|
||||||
|
* deletion, and retrieval operations.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The hash map uses separate chaining to handle collisions. Each bucket in the hash map is represented
|
||||||
|
* by a linked list that holds nodes containing key-value pairs. When multiple keys hash to the same index,
|
||||||
|
* they are stored in the same linked list.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The hash map automatically resizes itself when the load factor exceeds 0.5. 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.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param <K> the type of keys maintained by this hash map
|
||||||
|
* @param <V> the type of mapped values
|
||||||
|
*/
|
||||||
public class GenericHashMapUsingArrayList<K, V> {
|
public class GenericHashMapUsingArrayList<K, V> {
|
||||||
|
|
||||||
ArrayList<LinkedList<Node>> buckets;
|
private ArrayList<LinkedList<Node>> buckets; // Array list of buckets (linked lists)
|
||||||
private float lf = 0.5f;
|
private int size; // Number of key-value pairs in the hash map
|
||||||
private int size;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new empty hash map with an initial capacity of 10 buckets.
|
||||||
|
*/
|
||||||
public GenericHashMapUsingArrayList() {
|
public GenericHashMapUsingArrayList() {
|
||||||
buckets = new ArrayList<>();
|
buckets = new ArrayList<>();
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
@ -17,6 +39,13 @@ public class GenericHashMapUsingArrayList<K, V> {
|
|||||||
size = 0;
|
size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
public void put(K key, V value) {
|
||||||
int hash = Math.abs(key.hashCode() % buckets.size());
|
int hash = Math.abs(key.hashCode() % buckets.size());
|
||||||
LinkedList<Node> nodes = buckets.get(hash);
|
LinkedList<Node> nodes = buckets.get(hash);
|
||||||
@ -31,25 +60,36 @@ public class GenericHashMapUsingArrayList<K, V> {
|
|||||||
nodes.add(new Node(key, value));
|
nodes.add(new Node(key, value));
|
||||||
size++;
|
size++;
|
||||||
|
|
||||||
if ((float) size / buckets.size() > lf) {
|
// Load factor threshold for resizing
|
||||||
|
float loadFactorThreshold = 0.5f;
|
||||||
|
if ((float) size / buckets.size() > loadFactorThreshold) {
|
||||||
reHash();
|
reHash();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes the hash map by doubling the number of buckets and rehashing existing entries.
|
||||||
|
*/
|
||||||
private void reHash() {
|
private void reHash() {
|
||||||
ArrayList<LinkedList<Node>> old = buckets;
|
ArrayList<LinkedList<Node>> oldBuckets = buckets;
|
||||||
buckets = new ArrayList<>();
|
buckets = new ArrayList<>();
|
||||||
size = 0;
|
size = 0;
|
||||||
for (int i = 0; i < old.size() * 2; i++) {
|
for (int i = 0; i < oldBuckets.size() * 2; i++) {
|
||||||
buckets.add(new LinkedList<>());
|
buckets.add(new LinkedList<>());
|
||||||
}
|
}
|
||||||
for (LinkedList<Node> nodes : buckets) {
|
for (LinkedList<Node> nodes : oldBuckets) {
|
||||||
for (Node node : nodes) {
|
for (Node node : nodes) {
|
||||||
put(node.key, node.val);
|
put(node.key, node.val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
public V get(K key) {
|
||||||
int hash = Math.abs(key.hashCode() % buckets.size());
|
int hash = Math.abs(key.hashCode() % buckets.size());
|
||||||
LinkedList<Node> nodes = buckets.get(hash);
|
LinkedList<Node> nodes = buckets.get(hash);
|
||||||
@ -61,6 +101,11 @@ public class GenericHashMapUsingArrayList<K, V> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
public void remove(K key) {
|
||||||
int hash = Math.abs(key.hashCode() % buckets.size());
|
int hash = Math.abs(key.hashCode() % buckets.size());
|
||||||
LinkedList<Node> nodes = buckets.get(hash);
|
LinkedList<Node> nodes = buckets.get(hash);
|
||||||
@ -72,18 +117,36 @@ public class GenericHashMapUsingArrayList<K, V> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (target != null) {
|
||||||
nodes.remove(target);
|
nodes.remove(target);
|
||||||
size--;
|
size--;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
public boolean containsKey(K key) {
|
||||||
return get(key) != null;
|
return get(key) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of key-value pairs in this map.
|
||||||
|
*
|
||||||
|
* @return the number of key-value pairs
|
||||||
|
*/
|
||||||
public int size() {
|
public int size() {
|
||||||
return this.size;
|
return this.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the map, containing all key-value pairs.
|
||||||
|
*
|
||||||
|
* @return a string representation of the map
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
@ -96,15 +159,27 @@ public class GenericHashMapUsingArrayList<K, V> {
|
|||||||
builder.append(", ");
|
builder.append(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Remove trailing comma and space if there are any elements
|
||||||
|
if (builder.length() > 1) {
|
||||||
|
builder.setLength(builder.length() - 2);
|
||||||
|
}
|
||||||
builder.append("}");
|
builder.append("}");
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A private inner class representing a key-value pair (node) in the hash map.
|
||||||
|
*/
|
||||||
private class Node {
|
private class Node {
|
||||||
|
|
||||||
K key;
|
K key;
|
||||||
V val;
|
V val;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new Node with the specified key and value.
|
||||||
|
*
|
||||||
|
* @param key the key of the key-value pair
|
||||||
|
* @param val the value of the key-value pair
|
||||||
|
*/
|
||||||
Node(K key, V val) {
|
Node(K key, V val) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.val = val;
|
this.val = val;
|
||||||
|
@ -50,4 +50,47 @@ class GenericHashMapUsingArrayListTest {
|
|||||||
assertEquals("Washington DC", map.get(101));
|
assertEquals("Washington DC", map.get(101));
|
||||||
assertTrue(map.containsKey(46));
|
assertTrue(map.containsKey(46));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRemoveNonExistentKey() {
|
||||||
|
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
|
||||||
|
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() {
|
||||||
|
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
|
||||||
|
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() {
|
||||||
|
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
|
||||||
|
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() {
|
||||||
|
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
|
||||||
|
map.put("USA", "Washington DC");
|
||||||
|
map.put("Nepal", "Kathmandu");
|
||||||
|
String expected = "{USA : Washington DC, Nepal : Kathmandu}";
|
||||||
|
assertEquals(expected, map.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testContainsKey() {
|
||||||
|
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
|
||||||
|
map.put("USA", "Washington DC");
|
||||||
|
assertTrue(map.containsKey("USA"));
|
||||||
|
assertFalse(map.containsKey("Nepal"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user