package com.interview.basics.model.collection.hash;
import java.util.Iterator;
interface Entry<K, V> {
public int hash();
public K key();
public V value();
public Entry<K, V> next();
public void setValue(V value);
public void setNext(Entry<K, V> next);
}
public abstract class HashContainer<K, V> {
protected float loadFactor;
protected int capacity;
protected int count;
protected int threshold;
protected transient Entry<K,V>[] table;
protected abstract Entry<K, V>[] initCapacity(int capacity);
protected abstract Entry<K, V> getEntry(int hash, K key, V value, Entry<K, V> next);
@SuppressWarnings("unchecked")
public HashContainer(int initialCapacity, float loadFactor) {
if(initialCapacity < 0 || loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException(String.format("Illegal capacity or loadFactor [capacity=%s,loadFactor=%s]", initialCapacity, loadFactor));
this.capacity = initialCapacity;
this.loadFactor = loadFactor;
table = initCapacity(this.capacity);
this.threshold = (int)(this.capacity * this.loadFactor);
}
public HashContainer(){
this(100, 0.75f);
}
public synchronized V get(K key) {
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % this.capacity;
for(Entry<K,V> entry = this.table[index]; entry != null; entry = entry.next()){
if(entry.hash() == hash && entry.key().equals(key)){
return entry.value();
}
}
return null;
}
public synchronized V put(K key, V value) {
if (key == null || value == null)
throw new IllegalArgumentException("Null is not allowed for key or value");
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % this.capacity;
for(Entry<K,V> entry = this.table[index]; entry != null; entry = entry.next()){
if(entry.hash() == hash && entry.key().equals(key)){
V oldValue = entry.value();
entry.setValue(value);
return oldValue;
}
}
if(count >= threshold){
rehash();
index = (hash & 0x7FFFFFFF) % this.capacity;
}
Entry<K,V> entry = this.table[index];
table[index] = getEntry(hash, key, value, entry);
this.count ++;
return null;
}
@SuppressWarnings("unchecked")
private void rehash() {
int newCapacity = this.capacity * 2 + 1;
Entry<K,V>[] newTable = initCapacity(newCapacity);
for(int i = this.capacity - 1; i >= 0 ; i --){
for(Entry<K,V> entry = this.table[i]; entry != null; ) {
Entry<K,V> nextEntry = entry.next();
int index = (entry.hash() & 0x7FFFFFFF) % newCapacity;
entry.setNext(newTable[index]);
newTable[index] = entry;
entry = nextEntry;
}
}
this.capacity = newCapacity;
this.table = newTable;
this.threshold = (int) (this.capacity * this.loadFactor);
}
public synchronized V remove(K key){
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % this.capacity;
Entry<K,V> previous = null;
for(Entry<K,V> entry = this.table[index]; entry != null; previous=entry, entry = entry.next()){
if(entry.hash() == hash && entry.key().equals(key)){
V value = entry.value();
if(previous == null) {
this.table[index] = entry.next();
} else {
previous.setNext(entry.next());
}
this.count--;
entry = null;
return value;
}
}
return null;
}
public Iterator<Entry<K, V>> iterator(){
return new Iterator<Entry<K, V>>() {
int cursor = -1;
int tableIndex = -1;
Entry<K, V> entry = null;
@Override
public boolean hasNext() {
return cursor + 1 < count;
}
@Override
public Entry<K, V> next() {
cursor++;
if(entry!= null && entry.next() != null) {
entry = entry.next();
return entry;
}
while(++tableIndex < table.length && table[tableIndex] == null){};
entry = table[tableIndex];
return entry;
}
@Override
public void remove() {
if(entry.next() != null){
entry.setValue(entry.next().value());
entry.setNext(entry.next().next());
} else {
entry = null;
}
}
};
}
}