Rice University - Comp 212 - Intermediate Programming

Fall 2002

Lecture #34 - Hash Tables and Hash Functions


Hash Tables


The Problem: Collisions


Chaining


Performance


A Hash Table Implementation

package dict;

import lrs.*;
import listFW.*;

/**
 * An IDictionary implemented using a hash table.  Collisions are handled
 * using chaining.  The chains are implemented using DictLRS (code shown below).
 *
 * 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
 * @since 11/17/02 - Modified by DXN

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

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

                public Object nonEmptyCase(IList 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().execute(new IListAlgo() {
                    public Object emptyCase(IList host, Object input) {
                        return null;
                    }

                    public Object nonEmptyCase(IList 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();
    }
}
 
DictLRS.java

package dict;

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

/**
 * An implementation of IDictionary using an LRStruct to hold the
 * DictionaryPairs.
 */
public class DictLRS implements IDictionary {
    /*
     * A list of DictionaryPairs ordered by key
     */
    private LRStruct _lrs = new LRStruct();

    /**
     * Clears the contents of the dictionary leaving it empty.
     *
     * Implemented by replacing the existing LRStruct with a new,
     * empty one.
     */
    public void clear() {
	_lrs = new LRStruct();
    }

    /**
     * Returns true if the dictionary is empty and false otherwise.
     *
     * Implemented as a visitor to LRStruct.
     */
    public boolean isEmpty() {
	return ((Boolean)_lrs.execute(new IAlgo() {
	    public Object emptyCase(LRStruct host, Object input) {
		return Boolean.TRUE;
	    }

	    public Object nonEmptyCase(LRStruct host, Object input) {
		return Boolean.FALSE;
	    }
	}, null)).booleanValue();
    }

    /**
     * Returns an IList of DictionaryPairs corresponding to the entire
     * contents of the dictionary.
     *
     * Implemented as a visitor to LRStruct.
     */
    public IList elements() {
	return (IList)_lrs.execute(new IAlgo() {
	    IListFactory lf = new CompositeListFactory();

	    public Object emptyCase(LRStruct host, Object input) {
		return lf.makeEmptyList();
	    }

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

    /**
     * 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).
     *
     * Implemented as a visitor to LRStruct.
     */
    public DictionaryPair lookup(Comparable key) {
	return (DictionaryPair)_lrs.execute(new IAlgo() {
	    public Object emptyCase(LRStruct host, Object input) {
		return null;
	    }

	    public Object nonEmptyCase(LRStruct host, Object input) {
		DictionaryPair first = (DictionaryPair)host.getFirst();
		int result = first.getKey().compareTo(input);

		if (result > 0)		// host > input
		    return null;
		else if (result == 0)	// host == input
		    return first;
		else			// host < input
		    return host.getRest().execute(this, input);
	    }
	}, 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.
     *
     * Implemented as a visitor to LRStruct that inserts the key and
     * value in order.
     */
    public void insert(Comparable key, Object value) {
	_lrs.execute(new IAlgo() {
	    public Object emptyCase(LRStruct host, Object input) {
		return host.insertFront(input);
	    }

	    public Object nonEmptyCase(LRStruct host, Object input) {
		DictionaryPair first = (DictionaryPair)host.getFirst();
		int result = first.compareTo(input);

		if (result > 0)		// host > input
		    return host.insertFront(input);
		else if (result == 0)	// host == input
		    return host.setFirst(input);
		else			// host < input
		    return host.getRest().execute(this, input);
	    }
	}, new DictionaryPair(key, value));
    }

    /**
     * Removes the DictionaryPair with the given key and returns it.
     * If there is not a DictionaryPair with the given key, returns
     * null.
     *
     * Implemented as a visitor to LRStruct.
     */
    public DictionaryPair remove(Comparable key) {
	return (DictionaryPair)_lrs.execute(new IAlgo() {
	    public Object emptyCase(LRStruct host, Object input) {
		return null;
	    }

	    public Object nonEmptyCase(LRStruct host, Object input) {
		DictionaryPair first = (DictionaryPair)host.getFirst();
		int result = first.getKey().compareTo(input);

		if (result > 0)		// host > input
		    return null;
		else if (result == 0) {	// host == input
		    host.removeFront();
		    return first;
		} else			// host < input
		    return host.getRest().execute(this, input);
	    }
	}, key);
    }

    /**
     * Delegates the conversion to the LRStruct toString().
     */
    public String toString() {
	return _lrs.toString();
    }
}