import java.util.Random; // Cuckoo Hash table class // // CONSTRUCTION: a hashing function family and // an approximate initial size or default of 101 // // ******************PUBLIC OPERATIONS********************* // bool insert( x ) --> Insert x // bool remove( x ) --> Remove x // bool contains( x ) --> Return true if x is present // void makeEmpty( ) --> Remove all items // int size( ) --> Return number of items /** * Cuckoo hash table implementation of hash tables. * @author Mark Allen Weiss */ public class CuckooHashTable { /** * Construct the hash table. * @param hf the hash family */ public CuckooHashTable( HashFamily hf ) { this( hf, DEFAULT_TABLE_SIZE ); } /** * Construct the hash table. * @param hf the hash family * @param size the approximate initial size. */ public CuckooHashTable( HashFamily hf, int size ) { allocateArray( nextPrime( size ) ); doClear( ); hashFunctions = hf; numHashFunctions = hf.getNumberOfFunctions( ); } private Random r = new Random( ); private static final double MAX_LOAD = 0.40; private static final int ALLOWED_REHASHES = 1; private int rehashes = 0; private boolean insertHelper1( AnyType x ) { final int COUNT_LIMIT = 100; while( true ) { int lastPos = -1; int pos; for( int count = 0; count < COUNT_LIMIT; count++ ) { for( int i = 0; i < numHashFunctions; i++ ) { pos = myhash( x, i ); if( array[ pos ] == null ) { array[ pos ] = x; currentSize++; return true; } } // none of the spots are available. Kick out a random one int i = 0; do { pos = myhash( x, r.nextInt( numHashFunctions ) ); } while( pos == lastPos && i++ < 5 ); AnyType tmp = array[ lastPos = pos ]; array[ pos ] = x; x = tmp; } if( ++rehashes > ALLOWED_REHASHES ) { expand( ); // Make the table bigger rehashes = 0; } else rehash( ); } } private boolean insertHelper2( AnyType x ) { final int COUNT_LIMIT = 100; while( true ) { for( int count = 0; count < COUNT_LIMIT; count++ ) { int pos = myhash( x, count % numHashFunctions ); AnyType tmp = array[ pos ]; array[ pos ] = x; if( tmp == null ) return true; else x = tmp; } if( ++rehashes > ALLOWED_REHASHES ) { expand( ); // Make the table bigger rehashes = 0; } else rehash( ); } } /** * Insert into the hash table. If the item is * already present, return false. * @param x the item to insert. */ public boolean insert( AnyType x ) { if( contains( x ) ) return false; if( currentSize >= array.length * MAX_LOAD ) expand( ); return insertHelper1( x ); } private int myhash( AnyType x, int which ) { int hashVal = hashFunctions.hash( x, which ); hashVal %= array.length; if( hashVal < 0 ) hashVal += array.length; return hashVal; } private void expand( ) { rehash( (int) ( array.length / MAX_LOAD ) ); } private void rehash( ) { //System.out.println( "NEW HASH FUNCTIONS " + array.length ); hashFunctions.generateNewFunctions( ); rehash( array.length ); } private void rehash( int newLength ) { //System.out.println( "REHASH: " + array.length + " " + newLength + " " + currentSize ); AnyType [ ] oldArray = array; // Create a new double-sized, empty table allocateArray( nextPrime( newLength ) ); currentSize = 0; // Copy table over for( AnyType str : oldArray ) if( str != null ) insert( str ); } /** * Gets the size of the table. * @return number of items in the hash table. */ public int size( ) { return currentSize; } /** * Gets the length (potential capacity) of the table. * @return length of the internal array in the hash table. */ public int capacity( ) { return array.length; } /** * Method that searches all hash function places. * @param x the item to search for. * @return the position where the search terminates, or -1 if not found. */ private int findPos( AnyType x ) { for( int i = 0; i < numHashFunctions; i++ ) { int pos = myhash( x, i ); if( array[ pos ] != null && array[ pos ].equals( x ) ) return pos; } return -1; } /** * Remove from the hash table. * @param x the item to remove. * @return true if item was found and removed */ public boolean remove( AnyType x ) { int pos = findPos( x ); if( pos != -1 ) { array[ pos ] = null; currentSize--; } return pos != -1; } /** * Find an item in the hash table. * @param x the item to search for. * @return the matching item. */ public boolean contains( AnyType x ) { return findPos( x ) != -1; } /** * Make the hash table logically empty. */ public void makeEmpty( ) { doClear( ); } private void doClear( ) { currentSize = 0; for( int i = 0; i < array.length; i++ ) array[ i ] = null; } private static final int DEFAULT_TABLE_SIZE = 101; private final HashFamily hashFunctions; private final int numHashFunctions; private AnyType [ ] array; // The array of elements private int currentSize; // The number of occupied cells /** * Internal method to allocate array. * @param arraySize the size of the array. */ private void allocateArray( int arraySize ) { array = (AnyType[]) new Object[ arraySize ]; } /** * Internal method to find a prime number at least as large as n. * @param n the starting number (must be positive). * @return a prime number larger than or equal to n. */ protected static int nextPrime( int n ) { if( n % 2 == 0 ) n++; for( ; !isPrime( n ); n += 2 ) ; return n; } /** * Internal method to test if a number is prime. * Not an efficient algorithm. * @param n the number to test. * @return the result of the test. */ private static boolean isPrime( int n ) { if( n == 2 || n == 3 ) return true; if( n == 1 || n % 2 == 0 ) return false; for( int i = 3; i * i <= n; i += 2 ) if( n % i == 0 ) return false; return true; } // Simple main public static void main( String [ ] args ) { long cumulative = 0; final int NUMS = 2000000; final int GAP = 37; final int ATTEMPTS = 10; System.out.println( "Checking... (no more output means success)" ); for( int att = 0; att < ATTEMPTS; att++ ) { System.out.println( "ATTEMPT: " + att ); CuckooHashTable H = new CuckooHashTable<>( new StringHashFamily( 3 ) ); //QuadraticProbingHashTable H = new QuadraticProbingHashTable<>( ); long startTime = System.currentTimeMillis( ); for( int i = GAP; i != 0; i = ( i + GAP ) % NUMS ) H.insert( ""+i ); for( int i = GAP; i != 0; i = ( i + GAP ) % NUMS ) if( H.insert( ""+i ) ) System.out.println( "OOPS!!! " + i ); for( int i = 1; i < NUMS; i+= 2 ) H.remove( ""+i ); for( int i = 2; i < NUMS; i+=2 ) if( !H.contains( ""+i ) ) System.out.println( "Find fails " + i ); for( int i = 1; i < NUMS; i+=2 ) { if( H.contains( ""+i ) ) System.out.println( "OOPS!!! " + i ); } long endTime = System.currentTimeMillis( ); cumulative += endTime - startTime; if( H.capacity( ) > NUMS * 4 ) System.out.println( "LARGE CAPACITY " + H.capacity( ) ); } System.out.println( "Total elapsed time is: " + cumulative ); } }