Rice University - Comp 212 - Intermediate Programming

Spring 2004

Lecture #29 - Hash Tables and Hash Functions


Hash Tables


The Problem: Collisions


Chaining


Performance


Hash Table Implementations

package dict;

import java.lang.*;
import lrs.*;
import listFW.*;

/**
 * An IDictionary implemented using a hash table.  Collisions are handled
 * using chaining.  The chains are implemented using DictLRS.
 *
 * Uses the method hashCode() defined by class Object as the hash
 * function.  Any class may override this method with a new
 * implementation.
 *
 * @author Alan L. Cox
 * @since 03/28/03
 */
public class DictHash implements IDictionary {
    /*
     * Initialize _table to reference a single-element array of
     * IDictionary, containing in its single element a reference to an
     * empty DictLRS.
     */
    private IDictionary[] _table = { new DictLRS() };
    private int _tableOccupancy = 0;

     /*
     * An IList factory used to linearalize each internal DictLRS and
     * resize the _table array.

     */
    private IListFactory _lf;
    
     /*

     * An upper bound on the load factor.
     */    
    private double _loadFactor;

    public DictHash(IListFactory lf, double loadFactor) {
        _lf = lf;

        _loadFactor = loadFactor;
    }

    /**
     * Clears the contents of the dictionary leaving it empty.
     *
     * Implemented by replacing the existing LRStruct with a new,
     * empty one.
     */
    public void clear() {
        _table = new DictLRS[1];
        _table[0] = new DictLRS();
        _tableOccupancy = 0;
    }

    /**
     * Returns true if the dictionary is empty and false otherwise.
     */
    public boolean isEmpty() {
        return _tableOccupancy == 0;
    }

    /**
     * Returns an IList of DictionaryPairs corresponding to the entire
     * contents of the dictionary.
     *
     * Note that the elements are not in order.
     */
    public IList elements(IListFactory lf) {
        IList l = lf.makeEmptyList();

        for (int i = 0; i < _table.length; i++)
            l = (AList)_table[i].elements(lf).execute(new IListAlgo() {
                public Object emptyCase(IEmptyList host, Object input) {
                    return input;
                }

                public Object nonEmptyCase(INEList host, Object input) {
                    return lf.makeNEList(host.getFirst(),
                                         (IList)host.getRest().execute(this,
                                                                       input));
                }
            }, l);

        return l;
    }

    /**
     * Returns the DictionaryPair with the given key.  If there is not
     * a DictionaryPair with the given key, returns null.
     *
     * Returns a DictionaryPair rather than the value alone so that
     * the user can distinguish between not finding the key and
     * finding the pair (key, null).
     *
     * This method is O(1) in the expected case and O(n) in the worst
     * case.
     *
     * @param key the key to lookup
     * @return the DictionaryPair found
     */
    public DictionaryPair lookup(Comparable key) {
        int index = key.hashCode() % _table.length;

        return _table[index].lookup(key);
    }

    /**
     * Inserts the given key and value.  If the given key is already
     * in the dictionary, the given value replaces the key's old
     * value.
     *
     * This method is O(1) in both the expected case and the worst
     * case if we amortize the cost of doubling the hash table over
     * subsequent insert()'s.
     *
     * @param key the key to insert
     * @param value the value to insert
     */
    public void insert(Comparable key, Object value) {
        if (_tableOccupancy >= (_loadFactor * _table.length)) {
            int i;

            final IDictionary newTable[] = new IDictionary[2*_table.length];

            for (i = 0; i < newTable.length; i++)
                newTable[i] = new DictLRS();

            for (i = 0; i < _table.length; i++) {
                _table[i].elements(_lf).execute(new IListAlgo() {
                    public Object emptyCase(AList host, Object input) {
                        return null;
                    }

                    public Object nonEmptyCase(AList host, Object input) {
                        DictionaryPair pair = (DictionaryPair) host.getFirst();
                        int index = pair.getKey().hashCode() % newTable.length;

                        newTable[index].insert(pair.getKey(), pair.getValue());

                        return host.getRest().execute(this, input);
                    }
                }, null);
            }
            _table = newTable;
        }
        int index = key.hashCode() % _table.length;

        _tableOccupancy++;
        _table[index].insert(key, value);
    }

    /**
     * Removes the DictionaryPair with the given key and returns it.
     * If there is not a DictionaryPair with the given key, returns
     * null.
     *
     * This method is O(1) in the expected case and O(n) in the worst
     * case.
     *
     * @param key the key to remove
     * @return the DictionaryPair removed
     */
    public DictionaryPair remove(Comparable key) {
        int index = key.hashCode() % _table.length;

        DictionaryPair pair = _table[index].remove(key);

        if (pair != null)
            _tableOccupancy--;

        return pair;
    }

    /**
     * Returns a string representing the contents of the dictionary.
     */
    public String toString() {
        return elements(_lf).toString();
    }
}