// File: Table.java from the package edu.colorado.collections // Complete documentation is available from the Table link in: // http://www.cs.colorado.edu/~main/docs package edu.colorado.collections; /****************************************************************************** * A Table is an open-address hash table with a fixed capacity. * The purpose is to show students how an open-address hash table is * implemented. Programs should generally use java.util.Hashtable * rather than this hash table. * * Java Source Code for this class: * * http://www.cs.colorado.edu/~main/edu/colorado/collections/Table.java * * * @author Michael Main * (main@colorado.edu) * * @version Feb 10, 2016 ******************************************************************************/ public class Table { // Invariant of the Table class: // 1. The number of items in the table is in the instance variable manyItems. // 2. The preferred location for an element with a given key is at index // hash(key). If a collision occurs, then next-Index is used to search // forward to find the next open address. When an open address is found // at an index i, then the element itself is placed in data[i] and the // element’s key is placed at keys[i]. // 3. An index i that is not currently used has data[i] and key[i] set to // null. // 4. If an index i has been used at some point (now or in the past), then // hasBeenUsed[i] is true; otherwise it is false. private int manyItems; private Object[ ] keys; private Object[ ] data; private boolean[ ] hasBeenUsed; /** * Initialize an empty table with a specified capacity. * @param capacity * the capacity for this new open-address hash table * Postcondition: * This table is empty and has the specified capacity. * @exception OutOfMemoryError * Indicates insufficient memory for the specified capacity. **/ public Table(int capacity) { // The manyItems instance variable is automatically set to zero. // which is the correct initial value. The three arrays are allocated to // be the specified capacity. The boolean array is automatically // initialized to falses, and the other two arrays are automatically // initialized to all null. if (capacity <= 0) throw new IllegalArgumentException("Capacity is negative"); keys = new Object[capacity]; data = new Object[capacity]; hasBeenUsed = new boolean[capacity]; } /** * Determines whether a specified key is in this table. * @param key * the non-null key to look for * Precondition: * key cannot be null. * @return * true (if this table contains an object with the specified * key); false otherwise. Note that key.equals( ) * is used to compare the key to the keys that are in the * table. * @exception NullPointerException * Indicates that key is null. **/ public boolean containsKey(Object key) { return findIndex(key) != -1; } private int findIndex(Object key) // Postcondition: If the specified key is found in the table, then the return // value is the index of the specified key. Otherwise, the return value is -1. { int count = 0; int i = hash(key); while (count < data.length && hasBeenUsed[i]) { if (key.equals(keys[i])) return i; count++; i = nextIndex(i); } return -1; } /** Retrieves an object for a specified key. * @param key * the non-null key to look for * Precondition: * key cannot be null. * @return * a reference to the object with the specified key (if this * table contains an such an object); null otherwise. Note that * key.equals( ) is used to compare the key * to the keys that are in the table. * @exception NullPointerException * Indicates that key is null. **/ public Object get(Object key) { int index = findIndex(key); if (index == -1) return null; else return data[index]; } private int hash(Object key) // The return value is a valid index of the table’s arrays. The index is // calculated as the remainder when the absolute value of the key’s // hash code is divided by the size of the table’s arrays. { return Math.abs(key.hashCode( )) % data.length; } private int nextIndex(int i) // The return value is normally i+1. But if i+1 is data.length, then the // return value is zero instead. { if (i+1 == data.length) return 0; else return i+1; } /** * Add a new element to this table, using the specified key. * @param key * the non-null key to use for the new element * @param element * the new element that’s being added to this table * Precondition: * If there is not already an element with the specified key, * then this table’s size must be less than its capacity * (i.e., size() < capacity()). Also, neither key * nor element is null. * @return * If this table already has an object with the specified key, * then that object is replaced by element, and the return * value is a reference to the replaced object. Otherwise, the new * element is added with the specified key * and the return value is null. * @exception IllegalStateException * Indicates that there is no room for a new object in this table. * @exception NullPointerException * Indicates that key or element is null. **/ public Object put(Object key, Object element) { int index = findIndex(key); Object answer; if (index != -1) { // The key is already in the table. answer = data[index]; data[index] = element; return answer; } else if (manyItems < data.length) { // The key is not yet in this Table. index = hash(key); while (keys[index] != null) index = nextIndex(index); keys[index] = key; data[index] = element; hasBeenUsed[index] = true; manyItems++; return null; } else { // The table is full. throw new IllegalStateException("Table is full."); } } /** * Removes an object for a specified key. * @param key * the non-null key to look for * Precondition: * key cannot be null. * @return * If an object was found with the specified key, then that * object has been removed from this table and a copy of the removed object * is returned; otherwise, this table is unchanged and the null reference * is returned. Note that * key.equals( ) is used to compare the key * to the keys that are in the table. * @exception NullPointerException * Indicates that key is null. **/ public Object remove(Object key) { int index = findIndex(key); Object answer = null; if (index != -1) { answer = data[index]; keys[index] = null; data[index] = null; manyItems--; } return answer; } }