Skip to content

Commit bc6ea1e

Browse files
authored
Enhance docs, fix & add tests in `GenericHashMapUsingArrayL… (#5973)
1 parent f5bc2c8 commit bc6ea1e

File tree

2 files changed

+128
-10
lines changed

2 files changed

+128
-10
lines changed

src/main/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayList.java

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,34 @@
33
import java.util.ArrayList;
44
import java.util.LinkedList;
55

6+
/**
7+
* A generic implementation of a hash map using an array list of linked lists for collision resolution.
8+
* This class allows storage of key-value pairs with average-case constant time complexity for insertion,
9+
* deletion, and retrieval operations.
10+
*
11+
* <p>
12+
* The hash map uses separate chaining to handle collisions. Each bucket in the hash map is represented
13+
* by a linked list that holds nodes containing key-value pairs. When multiple keys hash to the same index,
14+
* they are stored in the same linked list.
15+
* </p>
16+
*
17+
* <p>
18+
* The hash map automatically resizes itself when the load factor exceeds 0.5. The load factor is defined
19+
* as the ratio of the number of entries to the number of buckets. When resizing occurs, all existing entries
20+
* are rehashed and inserted into the new buckets.
21+
* </p>
22+
*
23+
* @param <K> the type of keys maintained by this hash map
24+
* @param <V> the type of mapped values
25+
*/
626
public class GenericHashMapUsingArrayList<K, V> {
727

8-
ArrayList<LinkedList<Node>> buckets;
9-
private float lf = 0.5f;
10-
private int size;
28+
private ArrayList<LinkedList<Node>> buckets; // Array list of buckets (linked lists)
29+
private int size; // Number of key-value pairs in the hash map
1130

31+
/**
32+
* Constructs a new empty hash map with an initial capacity of 10 buckets.
33+
*/
1234
public GenericHashMapUsingArrayList() {
1335
buckets = new ArrayList<>();
1436
for (int i = 0; i < 10; i++) {
@@ -17,6 +39,13 @@ public GenericHashMapUsingArrayList() {
1739
size = 0;
1840
}
1941

42+
/**
43+
* Associates the specified value with the specified key in this map.
44+
* If the map previously contained a mapping for the key, the old value is replaced.
45+
*
46+
* @param key the key with which the specified value is to be associated
47+
* @param value the value to be associated with the specified key
48+
*/
2049
public void put(K key, V value) {
2150
int hash = Math.abs(key.hashCode() % buckets.size());
2251
LinkedList<Node> nodes = buckets.get(hash);
@@ -31,25 +60,36 @@ public void put(K key, V value) {
3160
nodes.add(new Node(key, value));
3261
size++;
3362

34-
if ((float) size / buckets.size() > lf) {
63+
// Load factor threshold for resizing
64+
float loadFactorThreshold = 0.5f;
65+
if ((float) size / buckets.size() > loadFactorThreshold) {
3566
reHash();
3667
}
3768
}
3869

70+
/**
71+
* Resizes the hash map by doubling the number of buckets and rehashing existing entries.
72+
*/
3973
private void reHash() {
40-
ArrayList<LinkedList<Node>> old = buckets;
74+
ArrayList<LinkedList<Node>> oldBuckets = buckets;
4175
buckets = new ArrayList<>();
4276
size = 0;
43-
for (int i = 0; i < old.size() * 2; i++) {
77+
for (int i = 0; i < oldBuckets.size() * 2; i++) {
4478
buckets.add(new LinkedList<>());
4579
}
46-
for (LinkedList<Node> nodes : buckets) {
80+
for (LinkedList<Node> nodes : oldBuckets) {
4781
for (Node node : nodes) {
4882
put(node.key, node.val);
4983
}
5084
}
5185
}
5286

87+
/**
88+
* Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
89+
*
90+
* @param key the key whose associated value is to be returned
91+
* @return the value associated with the specified key, or null if no mapping exists
92+
*/
5393
public V get(K key) {
5494
int hash = Math.abs(key.hashCode() % buckets.size());
5595
LinkedList<Node> nodes = buckets.get(hash);
@@ -61,6 +101,11 @@ public V get(K key) {
61101
return null;
62102
}
63103

104+
/**
105+
* Removes the mapping for the specified key from this map if present.
106+
*
107+
* @param key the key whose mapping is to be removed from the map
108+
*/
64109
public void remove(K key) {
65110
int hash = Math.abs(key.hashCode() % buckets.size());
66111
LinkedList<Node> nodes = buckets.get(hash);
@@ -72,18 +117,36 @@ public void remove(K key) {
72117
break;
73118
}
74119
}
75-
nodes.remove(target);
76-
size--;
120+
if (target != null) {
121+
nodes.remove(target);
122+
size--;
123+
}
77124
}
78125

126+
/**
127+
* Returns true if this map contains a mapping for the specified key.
128+
*
129+
* @param key the key whose presence in this map is to be tested
130+
* @return true if this map contains a mapping for the specified key
131+
*/
79132
public boolean containsKey(K key) {
80133
return get(key) != null;
81134
}
82135

136+
/**
137+
* Returns the number of key-value pairs in this map.
138+
*
139+
* @return the number of key-value pairs
140+
*/
83141
public int size() {
84142
return this.size;
85143
}
86144

145+
/**
146+
* Returns a string representation of the map, containing all key-value pairs.
147+
*
148+
* @return a string representation of the map
149+
*/
87150
@Override
88151
public String toString() {
89152
StringBuilder builder = new StringBuilder();
@@ -96,15 +159,27 @@ public String toString() {
96159
builder.append(", ");
97160
}
98161
}
162+
// Remove trailing comma and space if there are any elements
163+
if (builder.length() > 1) {
164+
builder.setLength(builder.length() - 2);
165+
}
99166
builder.append("}");
100167
return builder.toString();
101168
}
102169

170+
/**
171+
* A private inner class representing a key-value pair (node) in the hash map.
172+
*/
103173
private class Node {
104-
105174
K key;
106175
V val;
107176

177+
/**
178+
* Constructs a new Node with the specified key and value.
179+
*
180+
* @param key the key of the key-value pair
181+
* @param val the value of the key-value pair
182+
*/
108183
Node(K key, V val) {
109184
this.key = key;
110185
this.val = val;

src/test/java/com/thealgorithms/datastructures/hashmap/hashing/GenericHashMapUsingArrayListTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,47 @@ void testGenericHashmapWhichUsesArrayAndKeyIsIntegerValueIsString() {
5050
assertEquals("Washington DC", map.get(101));
5151
assertTrue(map.containsKey(46));
5252
}
53+
54+
@Test
55+
void testRemoveNonExistentKey() {
56+
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
57+
map.put("USA", "Washington DC");
58+
map.remove("Nepal"); // Attempting to remove a non-existent key
59+
assertEquals(1, map.size()); // Size should remain the same
60+
}
61+
62+
@Test
63+
void testRehashing() {
64+
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
65+
for (int i = 0; i < 20; i++) {
66+
map.put("Key" + i, "Value" + i);
67+
}
68+
assertEquals(20, map.size()); // Ensure all items were added
69+
assertEquals("Value5", map.get("Key5")); // Check retrieval after rehash
70+
}
71+
72+
@Test
73+
void testUpdateValueForExistingKey() {
74+
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
75+
map.put("USA", "Washington DC");
76+
map.put("USA", "New Washington DC"); // Updating value for existing key
77+
assertEquals("New Washington DC", map.get("USA"));
78+
}
79+
80+
@Test
81+
void testToStringMethod() {
82+
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
83+
map.put("USA", "Washington DC");
84+
map.put("Nepal", "Kathmandu");
85+
String expected = "{USA : Washington DC, Nepal : Kathmandu}";
86+
assertEquals(expected, map.toString());
87+
}
88+
89+
@Test
90+
void testContainsKey() {
91+
GenericHashMapUsingArrayList<String, String> map = new GenericHashMapUsingArrayList<>();
92+
map.put("USA", "Washington DC");
93+
assertTrue(map.containsKey("USA"));
94+
assertFalse(map.containsKey("Nepal"));
95+
}
5396
}

0 commit comments

Comments
 (0)