Hash Tables and Hash Functions


Hash Tables


The Problem: Collisions


Chaining


Performance


A Hash Table Implementation

package dict;

import java.lang.*;
import lrs.*;
import schemeFactory.*;

/**
 * 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 04/01/02
 */
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;

    private double _loadFactor;

    public DictHash(double loadFactor)
    {
        _loadFactor = loadFactor;
    }

    /*
     * An AList factory used by elements()
     */
    private IListFactory _lf = new CompositeListFactory();

    /**
     * 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 AList of DictionaryPairs corresponding to the entire
     * contents of the dictionary.
     *
     * Note that the elements are not in order.
     */
    public AList elements() {
        AList l = _lf.makeEmptyList();

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

                public Object nonEmptyCase(AList host, Object input) {
                    return _lf.makeNEList(host.getFirst(),
                                          (AList)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().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().toString();
    }
}