algorist/src/main/java/net/abhinavsarkar/algorist/hash/OpenAddressingHashTable.java

330 lines
9.2 KiB
Java

package net.abhinavsarkar.algorist.hash;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import net.abhinavsarkar.algorist.Map;
public class OpenAddressingHashTable<K, V> implements Map<K, V>
{
private static final Entry REMOVED = new Entry(0,0);
private Object[] store;
private final double loadFactor;
private final ProbingStrategy probingStrategy;
private Entry<K, V> head;
private Entry<K, V> tail;
private int size;
private int actualSize;
public OpenAddressingHashTable(int initialCapacity, double loadFactor, ProbingStrategy probingStrategy) {
this.store = new Object[nextPowerOf2(initialCapacity)];
this.loadFactor = loadFactor;
this.probingStrategy = probingStrategy;
}
public OpenAddressingHashTable(int initialCapacity, double loadFactor)
{
this(initialCapacity, loadFactor, new LinearProbing());
}
public OpenAddressingHashTable(int initialCapacity) {
this(initialCapacity, 0.75);
}
public OpenAddressingHashTable() {
this(16);
}
private static int nextPowerOf2(int num)
{
return num == 1 ? 1 : Integer.highestOneBit(num - 1) * 2;
}
@Override
public Optional<V> 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<K,V> entry = (Entry<K,V>) store[index];
if (entry == null)
{
return Optional.empty();
}
else if (entry == REMOVED && firstRemovedIndex == -1) {
firstRemovedIndex = index;
}
else if (entry.getKey().equals(key))
{
if (firstRemovedIndex != -1) {
store[firstRemovedIndex] = entry;
store[index] = REMOVED;
}
return Optional.of(entry.getVal());
}
trial++;
}
return Optional.empty();
}
@Override
public void put(K key, V val) {
if (actualSize >= loadFactor * store.length) {
resize();
}
doPut(new Entry<>(key, val), store, false);
}
private void resize() {
Object[] nStore = new Object[store.length * 2];
for (LinkedHashTableIterator iterator = new LinkedHashTableIterator(); iterator.hasNext();)
{
Entry<K, V> entry = iterator.next();
doPut(entry, nStore, true);
}
store = nStore;
actualSize = size;
}
private void doPut(Entry<K,V> nEntry, Object[] store, boolean resize) {
int trial = 0;
int baseIndex = calcIndex(nEntry.getKey(), store.length);
while (trial < store.length) {
int index = probingStrategy.nextIndex(baseIndex, trial, store.length);
Entry<K,V> entry = (Entry<K,V>) store[index];
if (entry == null || entry == REMOVED)
{
if (!resize)
{
linkNewEntry(nEntry);
size++;
if (entry != REMOVED) {
actualSize++;
}
}
store[index] = nEntry;
return;
}
else if (entry.getKey().equals(nEntry.getKey()))
{
if (!resize) {
linkUpdatedEntry(entry);
}
nEntry.next = entry.next;
nEntry.prev = entry.prev;
store[index] = nEntry;
return;
}
trial++;
}
throw new IllegalStateException("Impossible");
}
private void linkNewEntry(Entry<K, V> nEntry)
{
if (head == null)
{
head = nEntry;
}
else
{
tail.next = nEntry;
nEntry.prev = tail;
}
tail = nEntry;
}
private void linkUpdatedEntry(Entry<K, V> entry)
{
if (head == null)
{
head = entry;
} else if (entry == head) {
if (entry.next != null) {
head = entry.next;
tail.next = entry;
entry.prev = tail;
tail = entry;
}
} else if (entry != tail) {
entry.prev.next = entry.next;
entry.next.prev = entry.prev;
tail.next = entry;
entry.prev = tail;
entry.next = null;
tail = entry;
}
}
@Override
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<K,V> entry = (Entry<K,V>) store[index];
if (entry == null)
{
return;
}
else if (entry.getKey().equals(key))
{
unlinkEntry(entry);
store[index] = REMOVED;
size--;
return;
}
trial++;
}
}
private void unlinkEntry(Entry<K, V> 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;
}
}
public int size() {
return size;
}
private int calcIndex(K key, int size) {
return key.hashCode() % size;
}
public Iterator<Map.Entry<K,V>> iterator() {
return new LinkedHashTableIterator();
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (LinkedHashTableIterator iterator = new LinkedHashTableIterator(); iterator.hasNext();)
{
Entry<K, V> entry = iterator.next();
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<K, V> extends Map.Entry<K, V> {
private Entry<K, V> prev;
private Entry<K, V> next;
public Entry(K key, V val) {
super(key, val);
}
}
private class HashTableIterator implements Iterator<Entry<K,V>> {
private int index = 0;
private int size = store.length;
public boolean hasNext() {
return index < size;
}
public Entry<K,V> next() {
while (index < size) {
Entry<K,V> entry = (Entry<K,V>) store[index++];
if (entry != null && entry != REMOVED) {
return entry;
}
}
throw new NoSuchElementException();
}
}
private class LinkedHashTableIterator implements Iterator<Map.Entry<K,V>> {
private Entry<K,V> start = head;
public boolean hasNext() {
return start != null;
}
public Entry<K,V> next() {
if (start != null) {
Entry<K, V> entry = start;
start = start.next;
return entry;
}
throw new NoSuchElementException();
}
}
public static void main(String[] args)
{
OpenAddressingHashTable<String, String> table =
new OpenAddressingHashTable<>(2, 0.75, new LinearProbing());
table.put("a", "abhinav");
table.put("b", "batman");
table.put("c", "carol");
table.put("c", "carly");
table.remove("a");
System.out.println(table.size());
table.put("z", "zellman");
table.put("w", "walker");
System.out.println(table);
System.out.println(table.size());
table.remove("x");
table.remove("z");
System.out.println(table.size());
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.size());
table.put("w", "wanted");
System.out.println(table);
System.out.println(table.size());
System.out.println(table.get("A"));
System.out.println(table.get("B"));
System.out.println(table.get("C"));
}
}