diff --git a/.gitignore b/.gitignore index c38fa4e..7466b12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea *.iml +target/ diff --git a/src/main/java/net/abhinavsarkar/algorist/ChainingHashTable.java b/src/main/java/net/abhinavsarkar/algorist/ChainingHashTable.java index 0eabbba..b2370cb 100644 --- a/src/main/java/net/abhinavsarkar/algorist/ChainingHashTable.java +++ b/src/main/java/net/abhinavsarkar/algorist/ChainingHashTable.java @@ -21,7 +21,7 @@ public class ChainingHashTable return Optional.empty(); } else { return entries.forEach(h -> { - if (h.key == key) { + if (h.key.equals(key)) { return Optional.of(h.val); } return Optional.empty(); @@ -38,7 +38,7 @@ public class ChainingHashTable } else { LinkedList> entries = (LinkedList>) store[index]; Optional ret = entries.forEach(h -> { - if (h.key == key) + if (h.key.equals(key)) { h.val = val; return Optional.of(0); diff --git a/src/main/java/net/abhinavsarkar/algorist/OpenAddressingHashTable.java b/src/main/java/net/abhinavsarkar/algorist/OpenAddressingHashTable.java new file mode 100644 index 0000000..db52a27 --- /dev/null +++ b/src/main/java/net/abhinavsarkar/algorist/OpenAddressingHashTable.java @@ -0,0 +1,261 @@ +package net.abhinavsarkar.algorist; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Optional; + +public class OpenAddressingHashTable implements Iterable> +{ + + private static final Entry REMOVED = new Entry(0,0); + private Object[] store; + private ProbingStrategy probingStrategy; + private Entry head; + private Entry tail; + + public OpenAddressingHashTable(int initialCapacity, ProbingStrategy probingStrategy) { + this.store = new Object[initialCapacity]; + this.probingStrategy = probingStrategy; + } + + public Optional get(K key) { + int trial = 0; + int baseIndex = calcIndex(key, store.length); + int firstRemovedIndex = -1; + while (trial < store.length) + { + int index = probingStrategy.nextIndex(baseIndex, trial, store.length); + Entry entry = (Entry) store[index]; + if (entry == null) + { + return Optional.empty(); + } + else if (entry == REMOVED && firstRemovedIndex == -1) { + firstRemovedIndex = index; + } + else if (entry.key.equals(key)) + { + if (firstRemovedIndex != -1) { + store[firstRemovedIndex] = entry; + store[index] = REMOVED; + } + return Optional.of(entry.val); + } + trial++; + } + return Optional.empty(); + } + + public void put(K key, V val) { + if (!doPut(new Entry<>(key, val), store, false)) { + resize(); + put(key, val); + } + } + + private void resize() { + Object[] nStore = new Object[store.length * 2]; + for (Entry entry : this) + { + if (!doPut(entry, nStore, true)) + { + throw new IllegalStateException("Something is wrong"); + } + } + this.store = nStore; + } + + private boolean doPut(Entry nEntry, Object[] store, boolean resize) { + int trial = 0; + int baseIndex = calcIndex(nEntry.key, store.length); + while (trial < store.length) { + int index = probingStrategy.nextIndex(baseIndex, trial, store.length); + Entry entry = (Entry) store[index]; + if (entry == null || entry == REMOVED) + { + if (!resize) + { + addLink(nEntry); + } + + store[index] = nEntry; + return true; + } + else if (entry.key.equals(nEntry.key)) + { + entry.val = nEntry.val; + return true; + } + trial++; + } + return false; + } + + private void addLink(Entry nEntry) + { + if (head == null) + { + head = nEntry; + } + else + { + tail.next = nEntry; + nEntry.prev = tail; + } + tail = nEntry; + } + + public void remove(K key) { + int trial = 0; + int baseIndex = calcIndex(key, store.length); + while (trial < store.length) + { + int index = probingStrategy.nextIndex(baseIndex, trial, store.length); + Entry entry = (Entry) store[index]; + if (entry == null) + { + return; + } + else if (entry.key.equals(key)) + { + removeLink(entry); + store[index] = REMOVED; + return; + } + trial++; + } + } + + private void removeLink(Entry entry) + { + if (head == entry) { + head = entry.next; + } + if (tail == entry) { + tail = entry.prev; + } + if (entry.prev != null) { + entry.prev.next = entry.next; + } + if (entry.next != null) { + entry.next.prev = entry.prev; + } + } + + private int calcIndex(K key, int size) { + return key.hashCode() % size; + } + + public Iterator> iterator() { + return new LinkedHashTableIterator(); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + for (Entry entry : this) + { + sb.append(entry + ", "); + } + + sb.delete(sb.length()-2, sb.length()); + + return "{" + sb + "}"; + } + + public interface ProbingStrategy { + int nextIndex(int baseIndex, int probingTrial, int size); + } + + public static class LinearProbing implements ProbingStrategy { + public int nextIndex(int baseIndex, int probingTrial, int size) { + return (baseIndex + probingTrial) % size; + } + } + + public static class QuadraticProbing implements ProbingStrategy { + public int nextIndex(int baseIndex, int probingTrial, int size) { + return (baseIndex + probingTrial * probingTrial) % size; + } + } + + public static class Entry { + private K key; + private V val; + private Entry prev; + private Entry next; + public Entry(K key, V val) { + this.key = key; + this.val = val; + } + public String toString() { + return "<" + key + "," + val + ">"; + } + } + + private class HashTableIterator implements Iterator> { + private int index = 0; + private int size = store.length; + + public boolean hasNext() { + return index < size; + } + + public Entry next() { + while (index < size) { + Entry entry = (Entry) store[index++]; + if (entry != null && entry != REMOVED) { + return entry; + } + } + throw new NoSuchElementException(); + } + } + + private class LinkedHashTableIterator implements Iterator> { + private Entry start = head; + + public boolean hasNext() { + return start != null; + } + + public Entry next() { + if (start != null) { + Entry entry = start; + start = start.next; + return entry; + } + throw new NoSuchElementException(); + } + } + + public static void main(String[] args) + { + OpenAddressingHashTable table = + new OpenAddressingHashTable<>(4, new LinearProbing()); + table.put("a", "abhinav"); + table.put("b", "batman"); + table.put("c", "carol"); + table.put("c", "carly"); + table.put("z", "zellman"); + table.put("w", "walker"); + System.out.println(table); + + table.remove("x"); + table.remove("z"); + + System.out.println(table.get("a")); + System.out.println(table.get("b")); + System.out.println(table.get("c")); + System.out.println(table.get("w")); + System.out.println(table.get("z")); + + table.put("z", "zebra"); + System.out.println(table.get("z")); + System.out.println(table); + + System.out.println(table.get("A")); + System.out.println(table.get("B")); + System.out.println(table.get("C")); + } + +}