262 lines
7.2 KiB
Java
262 lines
7.2 KiB
Java
package net.abhinavsarkar.algorist;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.Optional;
|
|
|
|
public class OpenAddressingHashTable<K, V> implements Iterable<OpenAddressingHashTable.Entry<K, V>>
|
|
{
|
|
|
|
private static final Entry REMOVED = new Entry(0,0);
|
|
private Object[] store;
|
|
private ProbingStrategy probingStrategy;
|
|
private Entry<K, V> head;
|
|
private Entry<K, V> tail;
|
|
|
|
public OpenAddressingHashTable(int initialCapacity, ProbingStrategy probingStrategy) {
|
|
this.store = new Object[initialCapacity];
|
|
this.probingStrategy = probingStrategy;
|
|
}
|
|
|
|
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.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<K, V> entry : this)
|
|
{
|
|
if (!doPut(entry, nStore, true))
|
|
{
|
|
throw new IllegalStateException("Something is wrong");
|
|
}
|
|
}
|
|
this.store = nStore;
|
|
}
|
|
|
|
private boolean doPut(Entry<K,V> 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<K,V> entry = (Entry<K,V>) 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<K, V> 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<K,V> entry = (Entry<K,V>) store[index];
|
|
if (entry == null)
|
|
{
|
|
return;
|
|
}
|
|
else if (entry.key.equals(key))
|
|
{
|
|
removeLink(entry);
|
|
store[index] = REMOVED;
|
|
return;
|
|
}
|
|
trial++;
|
|
}
|
|
}
|
|
|
|
private void removeLink(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;
|
|
}
|
|
}
|
|
|
|
private int calcIndex(K key, int size) {
|
|
return key.hashCode() % size;
|
|
}
|
|
|
|
public Iterator<Entry<K,V>> iterator() {
|
|
return new LinkedHashTableIterator();
|
|
}
|
|
|
|
public String toString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (Entry<K,V> 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<K, V> {
|
|
private K key;
|
|
private V val;
|
|
private Entry<K, V> prev;
|
|
private Entry<K, V> next;
|
|
public Entry(K key, V val) {
|
|
this.key = key;
|
|
this.val = val;
|
|
}
|
|
public String toString() {
|
|
return "<" + 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<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<>(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"));
|
|
}
|
|
|
|
}
|