Skip to content

refactor: HashMap #5426

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 4 commits into from
Aug 28, 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
@@ -1,146 +1,275 @@
package com.thealgorithms.datastructures.hashmap.hashing;

public class HashMap {
/**
* A generic HashMap implementation that uses separate chaining with linked lists
* to handle collisions. The class supports basic operations such as insert, delete,
* and search, as well as displaying the contents of the hash map.
*
* @param <K> the type of keys maintained by this map
* @param <V> the type of mapped values
*/
public class HashMap<K, V> {
private final int hashSize;
private final LinkedList<K, V>[] buckets;

private final int hsize;
private final LinkedList[] buckets;

public HashMap(int hsize) {
buckets = new LinkedList[hsize];
for (int i = 0; i < hsize; i++) {
buckets[i] = new LinkedList();
// Java requires explicit initialization of each object
/**
* Constructs a HashMap with the specified hash size.
*
* @param hashSize the number of buckets in the hash map
*/
@SuppressWarnings("unchecked")
public HashMap(int hashSize) {
this.hashSize = hashSize;
// Safe to suppress warning because we are creating an array of generic type
this.buckets = new LinkedList[hashSize];
for (int i = 0; i < hashSize; i++) {
buckets[i] = new LinkedList<>();
}
this.hsize = hsize;
}

public int hashing(int key) {
int hash = key % hsize;
if (hash < 0) {
hash += hsize;
/**
* Computes the hash code for the specified key.
* Null keys are hashed to bucket 0.
*
* @param key the key for which the hash code is to be computed
* @return the hash code corresponding to the key
*/
private int computeHash(K key) {
if (key == null) {
return 0; // Use a special bucket (e.g., bucket 0) for null keys
}
return hash;
int hash = key.hashCode() % hashSize;
return hash < 0 ? hash + hashSize : hash;
}

public void insertHash(int key) {
int hash = hashing(key);
buckets[hash].insert(key);
/**
* Inserts the specified key-value pair into the hash map.
* If the key already exists, the value is updated.
*
* @param key the key to be inserted
* @param value the value to be associated with the key
*/
public void insert(K key, V value) {
int hash = computeHash(key);
buckets[hash].insert(key, value);
}

public void deleteHash(int key) {
int hash = hashing(key);

/**
* Deletes the key-value pair associated with the specified key from the hash map.
*
* @param key the key whose key-value pair is to be deleted
*/
public void delete(K key) {
int hash = computeHash(key);
buckets[hash].delete(key);
}

public void displayHashtable() {
for (int i = 0; i < hsize; i++) {
System.out.printf("Bucket %d :", i);
System.out.println(buckets[i].display());
}
/**
* Searches for the value associated with the specified key in the hash map.
*
* @param key the key whose associated value is to be returned
* @return the value associated with the specified key, or null if the key does not exist
*/
public V search(K key) {
int hash = computeHash(key);
Node<K, V> node = buckets[hash].findKey(key);
return node != null ? node.getValue() : null;
}

public static class LinkedList {

private Node first;

public LinkedList() {
first = null;
/**
* Displays the contents of the hash map, showing each bucket and its key-value pairs.
*/
public void display() {
for (int i = 0; i < hashSize; i++) {
System.out.printf("Bucket %d: %s%n", i, buckets[i].display());
}
}

public void insert(int key) {
if (isEmpty()) {
first = new Node(key);
return;
}
/**
* A nested static class that represents a linked list used for separate chaining in the hash map.
*
* @param <K> the type of keys maintained by this linked list
* @param <V> the type of mapped values
*/
public static class LinkedList<K, V> {
private Node<K, V> head;

Node temp = findEnd(first);
temp.setNext(new Node(key));
/**
* Inserts the specified key-value pair into the linked list.
* If the linked list is empty, the pair becomes the head.
* Otherwise, the pair is added to the end of the list.
*
* @param key the key to be inserted
* @param value the value to be associated with the key
*/
public void insert(K key, V value) {
Node<K, V> existingNode = findKey(key);
if (existingNode != null) {
existingNode.setValue(value); // Update the value, even if it's null
} else {
if (isEmpty()) {
head = new Node<>(key, value);
} else {
Node<K, V> temp = findEnd(head);
temp.setNext(new Node<>(key, value));
}
}
}

private Node findEnd(Node n) {
while (n.getNext() != null) {
n = n.getNext();
/**
* Finds the last node in the linked list.
*
* @param node the starting node
* @return the last node in the linked list
*/
private Node<K, V> findEnd(Node<K, V> node) {
while (node.getNext() != null) {
node = node.getNext();
}
return n;
return node;
}

public Node findKey(int key) {
if (!isEmpty()) {
Node temp = first;
if (temp.getKey() == key) {
/**
* Finds the node associated with the specified key in the linked list.
*
* @param key the key to search for
* @return the node associated with the specified key, or null if not found
*/
public Node<K, V> findKey(K key) {
Node<K, V> temp = head;
while (temp != null) {
if ((key == null && temp.getKey() == null) || (temp.getKey() != null && temp.getKey().equals(key))) {
return temp;
}

while ((temp = temp.getNext()) != null) {
if (temp.getKey() == key) {
return temp;
}
}
temp = temp.getNext();
}
return null;
}

public void delete(int key) {
if (!isEmpty()) {
if (first.getKey() == key) {
Node next = first.next;
first.next = null; // help GC
first = next;
} else {
delete(first, key);
}
/**
* Deletes the node associated with the specified key from the linked list.
* Handles the case where the key could be null.
*
* @param key the key whose associated node is to be deleted
*/
public void delete(K key) {
if (isEmpty()) {
return;
}
}

private void delete(Node n, int key) {
if (n.getNext().getKey() == key) {
if (n.getNext().getNext() == null) {
n.setNext(null);
} else {
n.setNext(n.getNext().getNext());
// Handle the case where the head node has the key to delete
if ((key == null && head.getKey() == null) || (head.getKey() != null && head.getKey().equals(key))) {
head = head.getNext();
return;
}

// Traverse the list to find and delete the node
Node<K, V> current = head;
while (current.getNext() != null) {
if ((key == null && current.getNext().getKey() == null) || (current.getNext().getKey() != null && current.getNext().getKey().equals(key))) {
current.setNext(current.getNext().getNext());
return;
}
} else {
delete(n.getNext(), key);
current = current.getNext();
}
}

/**
* Displays the contents of the linked list as a string.
*
* @return a string representation of the linked list
*/
public String display() {
return display(first);
return display(head);
}

private String display(Node n) {
if (n == null) {
return "null";
} else {
return n.getKey() + "->" + display(n.getNext());
/**
* Constructs a string representation of the linked list non-recursively.
*
* @param node the starting node
* @return a string representation of the linked list starting from the given node
*/
private String display(Node<K, V> node) {
StringBuilder sb = new StringBuilder();
while (node != null) {
sb.append(node.getKey()).append("=").append(node.getValue());
node = node.getNext();
if (node != null) {
sb.append(" -> ");
}
}
return sb.toString().isEmpty() ? "null" : sb.toString();
}

/**
* Checks if the linked list is empty.
*
* @return true if the linked list is empty, false otherwise
*/
public boolean isEmpty() {
return first == null;
return head == null;
}
}

public static class Node {
/**
* A nested static class representing a node in the linked list.
*
* @param <K> the type of key maintained by this node
* @param <V> the type of value maintained by this node
*/
public static class Node<K, V> {
private final K key;
private V value;
private Node<K, V> next;

private Node next;
private final int key;

public Node(int key) {
next = null;
/**
* Constructs a Node with the specified key and value.
*
* @param key the key associated with this node
* @param value the value associated with this node
*/
public Node(K key, V value) {
this.key = key;
this.value = value;
}

public Node getNext() {
return next;
/**
* Gets the key associated with this node.
*
* @return the key associated with this node
*/
public K getKey() {
return key;
}

public int getKey() {
return key;
/**
* Gets the value associated with this node.
*
* @return the value associated with this node
*/
public V getValue() {
return value;
}

public void setValue(V value) { // This method allows updating the value
this.value = value;
}

/**
* Gets the next node in the linked list.
*
* @return the next node in the linked list
*/
public Node<K, V> getNext() {
return next;
}

public void setNext(Node next) {
/**
* Sets the next node in the linked list.
*
* @param next the next node to be linked
*/
public void setNext(Node<K, V> next) {
this.next = next;
}
}
Expand Down
Loading