parent
a75f826e1b
commit
ba1023bca1
@ -1,2 +1,3 @@ |
||||
.idea |
||||
*.iml |
||||
target/ |
||||
|
@ -0,0 +1,261 @@ |
||||
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")); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue