/****************************************************************************** * Compilation: javac SeparateChainingHashST.java * Execution: java SeparateChainingHashST < input.txt * Dependencies: StdIn.java StdOut.java * Data files: https://algs4.cs.princeton.edu/34hash/tinyST.txt * * A symbol table implemented with a separate-chaining hash table. * ******************************************************************************/ /** * The {@code SeparateChainingHashST} class represents a symbol table of generic * key-value pairs. * It supports the usual put, get, contains, * delete, size, and is-empty methods. * It also provides a keys method for iterating over all of the keys. * A symbol table implements the associative array abstraction: * when associating a value with a key that is already in the symbol table, * the convention is to replace the old value with the new value. * Unlike {@link java.util.Map}, this class uses the convention that * values cannot be {@code null}—setting the * value associated with a key to {@code null} is equivalent to deleting the key * from the symbol table. *

* This implementation uses a separate chaining hash table. It requires that * the key type overrides the {@code equals()} and {@code hashCode()} methods. * The expected time per put, contains, or remove * operation is constant, subject to the uniform hashing assumption. * The size, and is-empty operations take constant time. * Construction takes constant time. *

* For additional documentation, see Section 3.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * For other implementations, see {@link ST}, {@link BinarySearchST}, * {@link SequentialSearchST}, {@link BST}, {@link RedBlackBST}, and * {@link LinearProbingHashST}, * * @author Robert Sedgewick * @author Kevin Wayne */ public class SeparateChainingHashST { private static final int INIT_CAPACITY = 4; private int n; // number of key-value pairs private int m; // hash table size private SequentialSearchST[] st; // array of linked-list symbol tables /** * Initializes an empty symbol table. */ public SeparateChainingHashST() { this(INIT_CAPACITY); } /** * Initializes an empty symbol table with {@code m} chains. * @param m the initial number of chains */ public SeparateChainingHashST(int m) { this.m = m; st = (SequentialSearchST[]) new SequentialSearchST[m]; for (int i = 0; i < m; i++) st[i] = new SequentialSearchST(); } // resize the hash table to have the given number of chains, // rehashing all of the keys private void resize(int chains) { SeparateChainingHashST temp = new SeparateChainingHashST(chains); for (int i = 0; i < m; i++) { for (Key key : st[i].keys()) { temp.put(key, st[i].get(key)); } } this.m = temp.m; this.n = temp.n; this.st = temp.st; } // hash function for keys - returns value between 0 and m-1 private int hashTextbook(Key key) { return (key.hashCode() & 0x7fffffff) % m; } // hash function for keys - returns value between 0 and m-1 (assumes m is a power of 2) // (from Java 7 implementation, protects against poor quality hashCode() implementations) private int hash(Key key) { int h = key.hashCode(); h ^= (h >>> 20) ^ (h >>> 12) ^ (h >>> 7) ^ (h >>> 4); return h & (m-1); } /** * Returns the number of key-value pairs in this symbol table. * * @return the number of key-value pairs in this symbol table */ public int size() { return n; } /** * Returns true if this symbol table is empty. * * @return {@code true} if this symbol table is empty; * {@code false} otherwise */ public boolean isEmpty() { return size() == 0; } /** * Returns true if this symbol table contains the specified key. * * @param key the key * @return {@code true} if this symbol table contains {@code key}; * {@code false} otherwise * @throws IllegalArgumentException if {@code key} is {@code null} */ public boolean contains(Key key) { if (key == null) throw new IllegalArgumentException("argument to contains() is null"); return get(key) != null; } /** * Returns the value associated with the specified key in this symbol table. * * @param key the key * @return the value associated with {@code key} in the symbol table; * {@code null} if no such value * @throws IllegalArgumentException if {@code key} is {@code null} */ public Value get(Key key) { if (key == null) throw new IllegalArgumentException("argument to get() is null"); int i = hash(key); return st[i].get(key); } /** * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. * * @param key the key * @param val the value * @throws IllegalArgumentException if {@code key} is {@code null} */ public void put(Key key, Value val) { if (key == null) throw new IllegalArgumentException("first argument to put() is null"); if (val == null) { delete(key); return; } // double table size if average length of list >= 10 if (n >= 10*m) resize(2*m); int i = hash(key); if (!st[i].contains(key)) n++; st[i].put(key, val); } /** * Removes the specified key and its associated value from this symbol table * (if the key is in this symbol table). * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} */ public void delete(Key key) { if (key == null) throw new IllegalArgumentException("argument to delete() is null"); int i = hash(key); if (st[i].contains(key)) n--; st[i].delete(key); // halve table size if average length of list <= 2 if (m > INIT_CAPACITY && n <= 2*m) resize(m/2); } // return keys in symbol table as an Iterable public Iterable keys() { Queue queue = new Queue(); for (int i = 0; i < m; i++) { for (Key key : st[i].keys()) queue.enqueue(key); } return queue; } /** * Unit tests the {@code SeparateChainingHashST} data type. * * @param args the command-line arguments */ public static void main(String[] args) { SeparateChainingHashST st = new SeparateChainingHashST(); for (int i = 0; !StdIn.isEmpty(); i++) { String key = StdIn.readString(); st.put(key, i); } // print keys for (String s : st.keys()) StdOut.println(s + " " + st.get(s)); } }