Implementing HashTable in Java

Hash table in the form similar to this structure
0 node -> node -> node
1 node -> node
2 null
3 node
4 node -> node
5 null
In the above, it means there is an ArrayList of size 6, as you can see there are 6 linked lists.
Each element of the ArrayList stores the head node of the list. Each linked list represents a bucket for storage.

A node in the linked list consists of a Key and a Value.

Adding a new node: Locate the bucket -> Insert the new node into the linked list
Locate the bucket: Get the hashCode of the Key -> Calculate the module of the hashCode by the size of the ArrayList -> Then use the result as the index to get the bucket(head node of a linked list) from the ArrayList
Locate the bucket: buckets.get(hashCode % size)

When the number of total key-value pair goes beyond the threshold, in this case 70% of the allocated ArrayList size, then double the ArrayList size and balance the data.

The code for implementing a hash table in Java.

import java.util.ArrayList;
/**
 * 
 * @param <K> K represents Key object
 * @param <V> V represents Value object
 */
public class HMap<K, V> {
	private class Node<K, V> {
		public K k;
		public V v;
		public Node<K, V> next;
		
		public Node(K k, V v) {
			this.k = k;
			this.v = v;
		}
	}
	
	private static double THRESHOLD = 0.7;
	private int size;
	private int allocationSize;
	private ArrayList<Node<K, V>> buckets = new ArrayList<>();
	
	public HMap() {
		size = 0;
		allocationSize = 2;
		allocateSize(allocationSize);
	}
	
	public int size() {
		return this.size;
	}
	
	public void clear() {
		this.buckets.clear();
		size = 0;
	}
	
	public boolean isEmpty() {
		return size == 0;
	}
	
	private void allocateSize(int size) {
		for (int i=0; i< size; i++) {
			buckets.add(null);
		}	
	}
	
	private boolean hasExceedThreshold() {
		return (1.0*size) / allocationSize >= THRESHOLD;
	}
	
	private int getBucketIndex(K k) {
		int hashNum = k.hashCode();
		int bucketIndex = hashNum % allocationSize; 
		return bucketIndex;
	}
	
	private void balanceData() {
		// save all of the existing node in an arraylist
		ArrayList<Node> nodes = new ArrayList<>();
		for (int i=0; i<allocationSize; i++) {
			Node head = buckets.get(i);
			while (head != null) {
				nodes.add(head);
				head = head.next;
			}
		}
		
		// reset the existing bucket
		buckets.clear();
		allocateSize(allocationSize);
		size = 0;
		for (int i=0; i<nodes.size(); i++) {
			Node node = nodes.get(i);
			add((K)node.k, (V) node.v);
		}
	}
	
	
	public void add(K k, V v) {
		// find the bucket index
		int bucketIndex = getBucketIndex(k); 
		
		// add new node at the beginning of the list
		Node newNode = new Node(k, v);
		Node head = buckets.get(bucketIndex);
		
		// if the k exists already, update the value and return
		Node cur = head;
		while (cur != null) {
			if (cur.k.equals(k)) {
				cur.v = v;
				return;
			}
			cur = cur.next;
		}
		
		// add new node at the beginning
		newNode.next = head;
		buckets.set(bucketIndex, newNode);
		size++;	
		
		// double the allocationSize if the size is >= THRESHOLD of the allocation size
		if (hasExceedThreshold()) {
			allocateSize(allocationSize);
			allocationSize = allocationSize * 2;
		
			balanceData();
		}
	}
	
	public V get(K k) {
		// find the bucket index
		int bucketIndex = getBucketIndex(k); 
		
		Node head = buckets.get(bucketIndex);
		if (head == null) return null;
		
		Node cur = head;
		while (cur != null) {
			if (cur.k.equals(k)) {
				return (V) cur.v;
			}
			cur = cur.next;
		}
		
		return null;
	}
	
	public void remove(K k) {
		int bucketIndex = getBucketIndex(k);
		
		Node head = buckets.get(bucketIndex);
		if (head == null) return;
		
		if (head.k.equals(k)) {
			buckets.set(bucketIndex, head.next);
			size--;
			return;
		}
		
		Node prev = head;
		Node cur = prev.next;
		while (cur != null) {
			if (cur.k.equals(k)) {
				prev.next = cur.next;
				size--;
				return;
			}
			prev = cur;
			cur = cur.next;
		}
	}

	
	public static void main(String args[]) {
		HMap<Integer, String> hMap = new HMap<>();
		hMap.add(1, "One");
		hMap.add(2, "Two");
		hMap.add(3, "Three");
		hMap.add(4, "Four");
		hMap.add(5, "Five");
		
		System.out.println("Expected Size: " + 5 + ", Actual: " + hMap.size());
		hMap.remove(4);
		System.out.println("Expected Size: " + 4 + ", Actual: " + hMap.size());
		
		System.out.println("Expected: One,   Actual: " + hMap.get(1));
		System.out.println("Expected: Two,   Actual: " + hMap.get(2));
		System.out.println("Expected: Three, Actual: " + hMap.get(3));
		System.out.println("Expected: null,  Actual: " + hMap.get(4));
		System.out.println("Expected: Five,  Actual: " + hMap.get(5));
		
		hMap.add(4, "Four"); hMap.add(4, "Fourr");
		System.out.println("Expected Size: " + 5 + ", Actual: " + hMap.size());
		System.out.println("Expected: Fourr,  Actual: " + hMap.get(4));
		
		hMap.clear();
		System.out.println("Expected Size: " + 0 + ", Actual: " + hMap.size());
		
		System.out.println("Expected isEmpty: true, Actual isEmpty: " + hMap.isEmpty());
	}
}

Search within Codexpedia

Custom Search

Search the entire web

Custom Search