Skip to content

refactor: Enhance docs, fix & add tests in `GenericHashMapUsingArrayL… #5973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,34 @@
import java.util.ArrayList;
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> {

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

/**
* Constructs a new empty hash map with an initial capacity of 10 buckets.
*/
public GenericHashMapUsingArrayList() {
buckets = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Expand All @@ -17,6 +39,13 @@ public GenericHashMapUsingArrayList() {
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) {
int hash = Math.abs(key.hashCode() % buckets.size());
LinkedList<Node> nodes = buckets.get(hash);
Expand All @@ -31,25 +60,36 @@ public void put(K key, V value) {
nodes.add(new Node(key, value));
size++;

if ((float) size / buckets.size() > lf) {
// Load factor threshold for resizing
float loadFactorThreshold = 0.5f;
if ((float) size / buckets.size() > loadFactorThreshold) {
reHash();
}
}

/**
* Resizes the hash map by doubling the number of buckets and rehashing existing entries.
*/
private void reHash() {
ArrayList<LinkedList<Node>> old = buckets;
ArrayList<LinkedList<Node>> oldBuckets = buckets;
buckets = new ArrayList<>();
size = 0;
for (int i = 0; i < old.size() * 2; i++) {
for (int i = 0; i < oldBuckets.size() * 2; i++) {
buckets.add(new LinkedList<>());
}
for (LinkedList<Node> nodes : buckets) {
for (LinkedList<Node> nodes : oldBuckets) {
for (Node node : nodes) {
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) {
int hash = Math.abs(key.hashCode() % buckets.size());
LinkedList<Node> nodes = buckets.get(hash);
Expand All @@ -61,6 +101,11 @@ public V get(K key) {
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) {
int hash = Math.abs(key.hashCode() % buckets.size());
LinkedList<Node> nodes = buckets.get(hash);
Expand All @@ -72,18 +117,36 @@ public void remove(K key) {
break;
}
}
nodes.remove(target);
size--;
if (target != null) {
nodes.remove(target);
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) {
return get(key) != null;
}

/**
* Returns the number of key-value pairs in this map.
*
* @return the number of key-value pairs
*/
public int size() {
return this.size;
}

/**
* Returns a string representation of the map, containing all key-value pairs.
*
* @return a string representation of the map
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
Expand All @@ -96,15 +159,27 @@ public String toString() {
builder.append(", ");
}
}
// Remove trailing comma and space if there are any elements
if (builder.length() > 1) {
builder.setLength(builder.length() - 2);
}
builder.append("}");
return builder.toString();
}

/**
* A private inner class representing a key-value pair (node) in the hash map.
*/
private class Node {

K key;
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) {
this.key = key;
this.val = val;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,47 @@ void testGenericHashmapWhichUsesArrayAndKeyIsIntegerValueIsString() {
assertEquals("Washington DC", map.get(101));
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"));
}
}
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy