// Hash Table using ** Open Addressing ** with Linear Probing public class HashTableLinProbe implements Dictionary { // Entry in the hash table using open addressing (no link field) private class Entry { K key; // Key V value; // Value public Entry(K k, V v) // Constructor { key = k; value = v; } public String toString() { return "(" + key + "," + value + ")"; } } public Entry[] bucket; // Hash table public int capacity; // capacity == bucket.length int NItems; // # items in hash table // MAD formula: ((a * HashCode + b) % p ) % M public int MAD_p; // Prime number in the Multiply Add Divide alg public int MAD_a; // Multiplier in the Multiply Add Divide alg public int MAD_b; // Offset in the Multiply Add Divide alg public int index_for_key; // = ((a * HashCode(key) + b) % p ) % M public Entry AVAILABLE = new Entry<>(null, null); // Special entry to // mark deleted entries public HashTableLinProbe(int M) { bucket = (Entry[]) new Entry[M]; // Create a hash table of size M // Can't use "new Entry[M]" ! capacity = bucket.length; // Capacity of this hash table NItems = 0; // # items in hash table MAD_p = 109345121; // We pick this prime number... MAD_a = 123; MAD_b = 456; } /* =============================================================== Hash function for the hash table: 1. uses the Hash Code from Java 2. uses the MAD compression function on the hash code hash index = [ (a*HashCode + b) % p ] % N =============================================================== */ public int hashValue(K key) { int x = key.hashCode(); // key has a built-in hashCode() !!! return ((Math.abs(x*MAD_a + MAD_b) % MAD_p) % capacity); } /* ****************************************************************** Hash table methods ****************************************************************** */ public int size() { return( NItems ); } public boolean isEmpty() { return( NItems == 0 ); } /* ------------------------------------------------------ put(k,v): if key k found: update corresponding value otherwise: insert (k,v) ------------------------------------------------------ */ public void put(K k, V v) { // Dictatic: good to show student the hash index int hashIdx = hashValue(k); System.out.println(k + " --> bucket: " + hashIdx); int i = hashIdx; int firstAvail = -1; // (k,v) must be inserted in first available bucket do { if ( bucket[i] == null ) // Key k not found - insert { if ( firstAvail == -1 ) bucket[i] = new Entry<>(k,v); else bucket[firstAvail] = new Entry<>(k,v); NItems++; return; } else if ( bucket[i] == AVAILABLE ) { if ( firstAvail == -1 ) firstAvail = i; } else if (bucket[i].key == k ) // Does bucket contains key k ? { bucket[i].value = v; return; // DONE ! } i = (i + 1)%capacity; // Check in next hash table bucket } while ( i != hashIdx ); // All entries searched ! if ( firstAvail == -1 ) System.out.println("**** Full ****"); else { bucket[firstAvail] = new Entry<>(k,v); NItems++; } } /* ------------------------------------------------------ get(k): if key k found, return corresponding value otherwise: return null ------------------------------------------------------ */ public V get(K k) { // Dictatic: good to show student the hash index int hashIdx = hashValue(k); System.out.println(k + " --> bucket: " + hashIdx); int i = hashIdx; do { if ( bucket[i] == null ) // Is bucket empty ? { return null; // Key k is not in hash table } else if ( bucket[i] == AVAILABLE ) { // DO NOT TEST bucket[i] !!! But we need to continue... } else if (bucket[i].key == k ) // Does bucket contains key k ? { return bucket[i].value; // FOUND - DONE ! } i = (i + 1)%capacity; // Check in next hash table bucket } while ( i != hashIdx ); // All entries searched ! return null; // No found } /* ---------------------------------------------------------------- remove(k): if key k found, remove Entry with k and return value otherwise: do nothing and return null ------------------------------------------------------ */ public V remove(K k) { // Dictatic: good to show student the hash index int hashIdx = hashValue(k); System.out.println(k + " --> bucket: " + hashIdx); int i = hashIdx; do { if ( bucket[i] == null ) // Is bucket empty ? { return null; // Key k is not in hash table } else if ( bucket[i] == AVAILABLE ) { // DO NOT TEST bucket[i] !!! But we need to continue... } else if (bucket[i].key == k ) // Does bucket contains key k ? { V retVal = bucket[i].value; bucket[i] = AVAILABLE; // Mark bucket as "available" NItems--; return retVal; // Return value } i = (i + 1)%capacity; // Check in next hash table bucket } while ( i != hashIdx ); // All entries searched ! return null; // No found } /* **************************************** Double the size of the hash table **************************************** */ public void doubleHashTable() { Entry[] oldBucket = bucket; // Double the size of the bucket bucket = (Entry[]) new Entry[2*oldBucket.length]; capacity = 2*oldBucket.length; // Rehash all entries by inserting them in the new hash table for ( int i = 0; i < oldBucket.length; i++ ) { if ( oldBucket[i] != null && oldBucket[i] != AVAILABLE ) this.put( oldBucket[i].key, oldBucket[i].value ); } } // Return string representing item in hash bucket e public String toString(Entry e) { if ( e == null ) return null; else if ( e == AVAILABLE ) return "AVAILABLE"; else return e.toString(); } public String toString() { String s = ""; for (int i = 0; i < bucket.length; i++) s = s + i + ": " + toString(bucket[i]) + "\n"; return s; } }