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")); } }