package DataStructures; import java.util.Iterator; import java.util.NoSuchElementException; /** *

An abstract class implementing the LookupTable interface * by means of a hash table. Collisions are resolved by probing.

* *

This class must be extended to implement a particular probing * algorithm for the findIndex method, such as double * hashing or quadratic probing.

* * @see ProbingHashTable#findIndex * @see DoubleHashingTable * @see QuadraticProbingTable * * @author Peter Williams */ public abstract class ProbingHashTable implements LookupTable { /** * Abstract method returning the index in the table of a key, * if present, or the index at which to insert it, if absent. * @param key the key to search for * @return the position where the search terminates */ protected abstract int findIndex(Object key); // the type of data entry stored in each element of the hash table /** * A class of objects returned by iteration over entries. * It represents a table entry as a (key, value) pair. */ protected static class Entry implements LookupTable.Entry { Object key; Object value; private boolean deleted; Entry(Object key, Object value) { this.key = key; this.value = value; deleted = false; } public Object key() { return key; } public Object value() { return value; } public String toString() { return "(" + key + ", " + value + ")"; } } /** * the underlying table of hash entries */ protected Entry[] table; private int size; // number of items currently stored private int occupied; // number of items currently stored or deleted private double loadFactor; // maximum permissible load factor private int threshold; // maximum size before rehashing /** * Constructs the hash table with given initial capacity and load factor. * The table will grow if necessary to ensure the load factor is not * exceeded. The load factor must lie between 0 and 1. Some * implementations of collision resolution may be more restrictive. * The implementation guarantees that the table length is always a prime. * @param capacity the initial capacity of the table * @param loadFactor the maximum loading before the table must grow * @exception IllegalArgumentException if the given load factor is * out of range */ ProbingHashTable(int capacity, double loadFactor) { if ((loadFactor <= 0.0) || (loadFactor > 1.0)) { throw new IllegalArgumentException("load factor out of range"); } this.loadFactor = loadFactor; capacity = nextPrime(capacity); // make sure table capacity is prime table = new Entry[capacity]; size = 0; occupied = 0; threshold = (int) (table.length * loadFactor); } public final boolean isEmpty() { return size == 0; } public final int size() { return size; } public final boolean contains(Object key) { Entry e = table[findIndex(key)]; return (e != null && !e.deleted); } public final void add(Object key, Object value) { int index = findIndex(key); Entry e = table[index]; if (e == null) { // make the new entry table[index] = new Entry(key, value); ++size; ++occupied; } else { // we must have found the key e.value = value; // overwrite the value if (e.deleted) { e.deleted = false; ++size; } } // now rehash if necessary if (occupied >= threshold) { rehash(); } } /* rehashing method for when the table occupancy grows too large */ private void rehash() { int capacity = nextPrime(table.length * 2); Entry[] oldTable = table; table = new Entry[capacity]; threshold = (int) (table.length * loadFactor); size = 0; occupied = 0; Entry e; for (int i = 0; i < oldTable.length; i++ ) { if ((e = oldTable[i]) != null && !e.deleted) { add(e.key, e.value); } } } public final void remove(Object key) { Entry e = table[findIndex(key)]; if (e != null && !e.deleted) { e.deleted = true; --size; } } public final Object lookup(Object key) { Entry e = table[findIndex(key)]; if (e != null && !e.deleted) { return e.value; } else { throw new NoSuchElementException(); } } public Iterator iterator() { return new Iterator() { private int index; private Entry current; private boolean removeOk; { index = 0; nextIndex(); removeOk = false; } public boolean hasNext() { return index < table.length; } public Object next() { if (index == table.length) { throw new NoSuchElementException(); } else { current = table[index++]; // use current index nextIndex(); // find next index for future use removeOk = true; return current; } } public void remove() { if (removeOk) { current.deleted = true; --size; removeOk = false; } else { throw new IllegalStateException(); } } private void nextIndex() { // skip over unused places while (index < table.length && (table[index] == null || table[index].deleted)) { ++index; } } }; } // returns the smallest prime number greater than or equal to n private final static int nextPrime(int n) { if (n < 2) return 2; if (n % 2 == 0) ++n; for (int i; ; n += 2) { for (i = 3; i * i <= n && n % i != 0; i += 2) {} if (i * i > n) return n; } } }