OpenHashTable.java

Download
1/*2 * OpenHashTable.java3 *4 * Computer Science S-22, Harvard University5 */6 7/*8 * A class that implements a hash table that employs open addressing9 * using either linear probing, quadratic probing, or double hashing.10 */11public class OpenHashTable implements HashTable {12    /* Private inner class for an entry in the hash table */13    private class Entry {14        private Object key;15        private LLQueue<Object> values;    // all of the values with this key16        17        private Entry(Object key, Object value) {18            this.key = key;19            values = new LLQueue<Object>();20            values.insert(value);21        }22    }23    24    // possible types of probing25    public static final int LINEAR = 0;26    public static final int QUADRATIC = 1;27    public static final int DOUBLE_HASHING = 2;28    public static final int NUM_PROBE_TYPES = 3;29    30    private Entry[] table;             // the hash table itself31    private int probeType = LINEAR;    // the type of probing32    33    public OpenHashTable(int size, int probeType) {34        if (size <= 0) {35            throw new IllegalArgumentException("size must be positive");36        }37        if (probeType < 0 || probeType >= NUM_PROBE_TYPES) {38            throw new IllegalArgumentException("invalid probeType: " + probeType);39        }40        41        table = new Entry[size];42        this.probeType = probeType;43    }44    45    /*46     * Constructor for a hash table of the specified size that uses double hashing47     */ 48    public OpenHashTable(int size) {49        // call the other constructor to do the work50        this(size, DOUBLE_HASHING);51    }52    53    /* first hash function */54    public int h1(Object key) {55        int h1 = key.hashCode() % table.length;56        if (h1 < 0) {57            h1 += table.length;58        }59        return h1;60    }61    62    /* second hash function */63    public int h2(Object key) {64        int h2 = key.hashCode() % 5;65        if (h2 < 0) {66            h2 += 11;67        }68        h2 += 5;69        return h2;70    }71    72    /* 73     * probeIncr - returns the amount by which the current index74     * should be incremented to obtain the next element in the probe75     * sequence if we have already checked numChecked positions76     * and h2 is the value of the second hash function77     */78    private int probeIncr(int numChecked, int h2) {79       if (numChecked <= 0) {80          return 0;81       } else if (probeType == LINEAR) {82           return 1;83       } else if (probeType == QUADRATIC) {84           return (2*numChecked - 1);85       } else {   //  DOUBLE_HASHING:86           return h2;87       }88    }89    90    /*91     * probe - attempt to find a slot in the hash table for the specified key.92     *93     * If key is currently in the table, it returns the index of the entry.94     * If key isn't in the table, it returns the index of the first empty cell95     * in the table.96     * If overflow occurs, it returns -1.97     */98    private int probe(Object key) {99        int i = h1(key);    // first hash function100        int h2 = h2(key);   // second hash function101        int numChecked = 1;102        103        // keep probing until we get an empty position or a match104        while (table[i] != null && !key.equals(table[i].key)) {105            if (numChecked == table.length) {106                return -1;107            }108            109            i = (i + probeIncr(numChecked, h2)) % table.length;110            numChecked++;111        }112        113        return i;114    }115    116    /*117     * insert - insert the specified (key, value) pair in the hash table.118     * Returns true if the pair can be added and false if there is overflow.119     */120    public boolean insert(Object key, Object value) {121        if (key == null) {122            throw new IllegalArgumentException("key must be non-null");123        }124        125        int i = h1(key); 126        int h2 = h2(key);127        int numChecked = 1;128        int firstRemoved = -1;129        130        while (table[i] != null && !key.equals(table[i].key)) {131            // record the index of the first removed cell we see132            if (table[i].key == null && firstRemoved == -1) {133                firstRemoved = i;134            }135            136            if (numChecked == table.length) {137                break;138            }139            140            i = (i + probeIncr(numChecked, h2)) % table.length;141            numChecked++;142        }143        144        if (table[i] != null && key.equals(table[i].key)) {145            table[i].values.insert(value);146        } else if (firstRemoved != -1) {147            table[firstRemoved] = new Entry(key, value);148        } else if (table[i] == null) {149            table[i] = new Entry(key, value);150        } else {151            return false;152        }153        154        return true;155    }156    157    /*158     * search - search for the specified key and return the159     * associated collection of values, or null if the key 160     * is not in the table161     */162    public Queue<Object> search(Object key) {163        if (key == null) {164            throw new IllegalArgumentException("key must be non-null");165        }166        167        int i = probe(key);168        169        if (i == -1 || table[i] == null) {170            return null;171        } else {172            return table[i].values;173        }174    }175    176    /* 177     * remove - remove from the table the entry for the specified key178     * and return the associated collection of values, or null if the key 179     * is not in the table180     */181    public Queue<Object> remove(Object key) {182        if (key == null) {183            throw new IllegalArgumentException("key must be non-null");184        }185            186        int i = probe(key);187        if (i == -1 || table[i] == null) {188            return null;189        }190        191        LLQueue<Object> removedVals = table[i].values;192        table[i].key = null;193        table[i].values = null;194        return removedVals;195    }196}