diff --git a/map/HashTableMap.java b/map/HashTableMap.java new file mode 100644 index 0000000..891e41a --- /dev/null +++ b/map/HashTableMap.java @@ -0,0 +1,185 @@ +package map; + + +import position.NodePositionList; +import position.PositionList; +import priorityqueue.Entry; + +import java.security.InvalidKeyException; + +/** + * A hash table data structure that uses linear probing to handle + * collisions. The hash function uses the built-in hashCode method + * and the multiply-add-and-divide method. The load factor is alwyas + * kept less than or equal to 0.5. When the load factor reaches 0.5, + * the entries are rehashed into a new bucket array with twice the + * capacity. + * + * @author Roberto Tamassia, Michael Goodrich, Eric Zamore + */ + +public class HashTableMap implements Map { + + public static class HashEntry implements Entry { + protected K key; + protected V value; + public HashEntry(K k, V v) { key = k; value = v; } + public V getValue() { return value; } + public K getKey() { return key; } + public V setValue(V val) { + V oldValue = value; + value = val; + return oldValue; + } + public boolean equals(Object o) { + HashEntry ent; + try { ent = (HashEntry) o; } + catch (ClassCastException ex) { return false; } + return (ent.getKey() == key) && (ent.getValue() == value); + } + + public String toString() { + return "(" + key + "," + value + ")"; + } + + } + protected Entry AVAILABLE = new HashEntry(null, null); + protected int n = 0; // number of entries in the dictionary + protected int prime, capacity; // prime factor and capacity of bucket array + protected Entry[] bucket;// bucket array + protected long scale, shift; // the shift and scaling factors + + /** Creates a hash table with prime factor 109345121 and capacity 1000. */ + public HashTableMap() { this(109345121,1000); } + + /** Creates a hash table with prime factor 109345121 and given capacity. */ + public HashTableMap(int cap) { this(109345121, cap); } + + /** Creates a hash table with the given prime factor and capacity. */ + public HashTableMap(int p, int cap) { + prime = p; + capacity = cap; + bucket = (Entry[]) new Entry[capacity]; // safe cast + java.util.Random rand = new java.util.Random(); + scale = rand.nextInt(prime-1) + 1; + shift = rand.nextInt(prime); + } + + /** Determines whether a key is valid. */ + protected void checkKey(K k) throws InvalidKeyException{ + if (k == null) throw new InvalidKeyException("Invalid key: null."); + } + + /** Hash function applying MAD method to default hash code. */ + public int hashValue(K key) { + return (int) ((Math.abs(key.hashCode()*scale + shift) % prime) % capacity); + } + + /** Returns the number of entries in the hash table. */ + public int size() { return n; } + + /** Returns whether or not the table is empty. */ + public boolean isEmpty() { return (n == 0); } + + /** Returns an iterable object containing all of the keys. */ + public Iterable keys() { + PositionList keys = new NodePositionList(); + for (int i=0; i e = bucket[i]; + if ( e == null) { + if (avail < 0) + avail = i; // key is not in table + break; + } + if (key.equals(e.getKey())) // we have found our key + return i; // key found + if (e == AVAILABLE) { // bucket is deactivated + if (avail < 0) + avail = i; // remember that this slot is available + } + i = (i + 1) % capacity; // keep looking + } while (i != j); + return -(avail + 1); // first empty or available slot + } + + /** Returns the value associated with a key. */ + public V get (K key) throws InvalidKeyException { + int i = findEntry(key); // helper method for finding a key + if (i < 0) return null; // there is no value for this key, so reutrn null + return bucket[i].getValue(); // return the found value in this case + } + + /** Put a key-value pair in the map, replacing previous one if it exists. */ + public V put (K key, V value) throws InvalidKeyException { + int i = findEntry(key); //find the appropriate spot for this entry + if (i >= 0) // this key has a previous value + return ((HashEntry) bucket[i]).setValue(value); // set new value + if (n >= capacity/2) { + rehash(); // rehash to keep the load factor <= 0.5 + i = findEntry(key); //find again the appropriate spot for this entry + } + bucket[-i-1] = new HashEntry(key, value); // convert to proper index + n++; + return null; // there was no previous value + } + + /** Doubles the size of the hash table and rehashes all the entries. */ + protected void rehash() throws InvalidKeyException{ + capacity = 2*capacity; + Entry[] old = bucket; + bucket = (Entry[]) new Entry[capacity]; // new bucket is twice as big + java.util.Random rand = new java.util.Random(); + scale = rand.nextInt(prime-1) + 1; // new hash scaling factor + shift = rand.nextInt(prime); // new hash shifting factor + for (int i=0; i e = old[i]; + if ((e != null) && (e != AVAILABLE)) { // a valid entry + int j = - 1 - findEntry(e.getKey()); + bucket[j] = e; + } + } + } + + /** Removes the key-value pair with a specified key. */ + public V remove (K key) throws InvalidKeyException { + int i = findEntry(key); // find this key first + if (i < 0) return null; // nothing to remove + V toReturn = bucket[i].getValue(); + bucket[i] = AVAILABLE; // mark this slot as deactivated + n--; + return toReturn; + } + + /** Returns an iterable object containing all of the entries. */ + public Iterable> entries() { + PositionList> entries = new NodePositionList>(); + for (int i=0; i values() { + PositionList values = new NodePositionList(); + for (int i=0; i