// Hunter Schafer, CSE 143 // Hash table implementation of a set, an unordered collection without duplicates. // The hash table stores values at indexes determined by a hashing function. // O(1) average time to add, remove, search. // This complete version uses chaining for collision resolution. public class HashSet implements Set { private int size; private Object[] elementData; public static final int DEFAULT_CAPACITY = 10; // Builds a new HashSet of default capacity public HashSet() { this(DEFAULT_CAPACITY); } // Builds a new HashSet of the specified capacity public HashSet(int capacity) { elementData = new Object[capacity]; } // Adds the given value to the set // Pre: There is nothing at indexOf(value) public boolean add(E value) { if (!contains(value)) { int i = indexOf(value); elementData[i] = new HashNode(value, (HashNode) elementData[i]); size++; checkLoad(); return true; } return false; } // Removes the given value from the set // If the set does not contain the given value, the set is unmodified. public boolean remove(E value) { if (contains(value)) { int i = indexOf(value); HashNode element = (HashNode) elementData[i]; if (element.data.equals(value)) { element = element.next; } else { HashNode current = element; while (!current.next.data.equals(value)) { current = current.next; } current.next = current.next.next; } size--; return true; } return false; } // Returns whether the given value is in the set public boolean contains(E value) { int i = indexOf(value); HashNode current = (HashNode) elementData[i]; while (current != null) { if (current.data.equals(value)) { return true; } current = current.next; } return false; } // Returns the size of the set public int size() { return size; } // Returns true is this set does not contain any elements, // false otherwise public boolean isEmpty() { return size == 0; } // Returns the String representation of this set. public String toString() { if (isEmpty()) { return "[]"; } else { String result = "["; // visit each index in the array for (int i = 0; i < elementData.length; i++) { HashNode current = (HashNode) elementData[i]; while (current != null) { result += current.data + ", "; current = current.next; } } return result.substring(0, result.length() - 2) + "]"; } } // Uses the hashing function to find the index of a value. private int indexOf(E value) { return Math.abs(value.hashCode() % elementData.length); } // Checks the load and resizes this array if the load exceeds the load // factor private void checkLoad() { if ((double) size / elementData.length >= 0.75) { HashNode[] newTable = (HashNode[]) new Object[elementData.length * 2]; for (int i = 0; i < elementData.length; i++) { while (elementData[i] != null) { HashNode node = (HashNode) elementData[i]; E value = node.data; int newIndex = Math.abs(value.hashCode() % newTable.length); elementData[i] = node.next; // remove the value // from the old table node.next = newTable[newIndex]; // add to the new table newTable[newIndex] = node; } } elementData = newTable; } } private class HashNode { public E data; public HashNode next; // Build a new node with the given data and next reference public HashNode(E data, HashNode next) { this.data = data; this.next = next; } } }