Rice University - Comp 212 - Intermediate Programming

Spring 2008

Lecture #28 - Hash Tables and Hash Functions


Hash Tables


The Problem: Collisions


Chaining


Performance


Hash Table Implementations

package genDict;

import genListFW.*;
/**
 * 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
 * @author Dung X. Nguyen ("generifier")
 * @since 03/23/05
 */

public class DictHash<K, V> implements IDictionary<K, V> {
    /*
    * Initialize _table to reference a single-element array of
    * IDictionary, containing in its single element a reference to an
    * empty DictLRS.
    */

    private IDictionary<K, V>[] _table = new DictLRS[1];
    private int _tableOccupancy = 0;
 
    /*
    * An IList factory used to linearalize each internal DictLRS and
    * resize the _table array.
    */

    private IListFactory< DictPair<K, V> > _lf;

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

    public DictHash(IListFactory< DictPair<K, V> > lf, double loadFactor) {
        _table[0] = new DictLRS<K, V>();
        _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<K, V>();
        _tableOccupancy = 0;
    }

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

    /**
    * Returns false always.
    */
    public boolean isFull() {
        return false;
    }

    /**
    * Returns an IList of DictPair<K, V>s corresponding to the entire
    * contents of the dictionary.
    *
    * Note that the elements are not in order.
    */
    public IList< DictPair<K, V> > elements(final IListFactory< DictPair<K, V> > lf) {
        IList< DictPair<K, V> > l = lf.makeEmptyList();
        for (int i = 0; i < _table.length; i++)
        l = _table[i].elements(lf).execute(new IListAlgo<DictPair<K, V>, IList< DictPair<K, V> >, IList< DictPair<K, V> > >() {
            public IList< DictPair<K, V> > emptyCase(IMTList<? extends DictPair<K, V> > host, IList< DictPair<K, V> >... input) {
                return input[0];
            }
            public IList< DictPair<K, V> > nonEmptyCase(INEList<? extends DictPair<K, V> > host, IList< DictPair<K, V> >... input) {
                return lf.makeNEList(host.getFirst(),(IList< DictPair<K, V> >)host.getRest().execute(this, input[0]));
            }
        }, l);
        return l;
}
 
    /**
    * Returns the DictPair<K, V> with the given key. If there is not
    * a DictPair<K, V> with the given key, returns null.
    *
    * Returns a DictPair<K, V> 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 DictPair<K, V> found
    */

    public DictPair<K, V> lookup(K 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 the expected case as we amortize the cost
    * of doubling the hash table over subsequent insert()'s.
    * The worst case is O(n) since no duplicates are allowed.
    *
    * @param key the key to insert
    * @param value the value to insert
    */

    public void insert(K key, V 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<K, V>();
            for (i = 0; i < _table.length; i++) {
                _table[i].elements(_lf).execute(new IListAlgo<DictPair<K, V>, Object, Object>() {
                    public Object emptyCase(IMTList<? extends DictPair<K, V> > host, Object... nu) {
                        return null;
                    }
                    public Object nonEmptyCase(INEList<? extends DictPair<K, V> > host, Object... nu) {
                        DictPair<K, V> pair = (DictPair<K, V>) host.getFirst();
                        int index = pair.getKey().hashCode() % newTable.length;
                        newTable[index].insert(pair.getKey(), pair.getValue());
                        return host.getRest().execute(this);
                    }
                });
            }
            _table = newTable;
        }
        int index = key.hashCode() % _table.length;
        _tableOccupancy++;
        _table[index].insert(key, value);
    }

    /**
    * Removes the DictPair<K, V> with the given key and returns it.
    * If there is not a DictPair<K, V> 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 DictPair<K, V> removed
    */

    public DictPair<K, V> remove(K key) {
        int index = key.hashCode() % _table.length;
        DictPair<K, V> 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();
    }
}