0

Post Details
In a data structures course, I was given Java source code for a "quadratic probing hash table" class and asked to implement a generic map (with get and put methods) and store the key/definition pairs in a hash table. I understand the material when reading the book but find it difficult to implement in a programming language (Java). I think part of the problem is understanding exactly what the question requires and part is deficiency in Java programming experience. I'm hoping to receive some suggestions for how I can approach problems like this and fill in whatever Java knowledge I'm missing.

Some questions I've had
What is the function of the hash table class in relation to the generic map I'm supposed to create? The hash table has several methods including get, insert, remove, rehash, etc... Is the purpose of the hash table to generate a hash value to use as a key in the map class? Are keys and definitions stored in the hash table or will they be stored in the map? What's the point of making a map if the hash table already does all of this?

Can someone help me understand how to approach problems like this? What are some references that might help me, either specifically with this question or with understanding how to effectively and methodically complete this type of exercise?

I appreciate whatever help I can get. I'm including code from the book to help illustrate the problem.

Quadratic Probing Hash Table Code From Textbook

public class QuadraticProbingHashTable<AnyType> {

    public QuadraticProbingHashTable() {
         this(DEFAULT_TABLE_SIZE);
     }

     public QuadraticProbingHashTable(int size) {
         allocateArray(size);
         doClear();
     }

     public boolean insert(AnyType x) {
         int currentPos = findPos(x);
         if(isActive(currentPos)) return false;

         array[currentPos] = new HashEntry<>(x, true);
         theSize++;

         if(++occupied > array.length / 2) rehash();

         return true;
     }

     private void rehash() {
         HashEntry<AnyType>[] oldArray = array;

         allocateArray(2 * oldArray.length);
         occupied = 0;
         theSize = 0;

         for(HashEntry<AnyType> entry : oldArray)
             if(entry != null && entry.isActive) insert(entry.element);
     }

     private int findPos(AnyType x) {
         int offset = 1;
         int currentPos = myhash(x);

         while(array[currentPos] != null && !array[currentPos].element.equals(x)) {
             currentPos += offset;
             offset += 2;
             if(currentPos >= array.length) currentPos -= array.length;
         }

         return currentPos;
     }

     public boolean remove(AnyType x) {
         int currentPos = findPos(x);
         if(isActive(currentPos)) {
             array[currentPos].isActive = false;
             theSize--;
             return true;
         } else return false;
     }

     public int size() {
         return theSize;
     }

     public int capacity() {
         return array.length;
     }

     public boolean contains(AnyType x) {
         int currentPos = findPos(x);
         return isActive(currentPos);
     }

     public AnyType get(AnyType x) {
        int currentPos = findPos(x);
        if(isActive(currentPos)) return array[currentPos].element;
        else return null;
     }

     private boolean isActive(int currentPos) {
         return array[currentPos] != null && array[currentPos].isActive;
     }

     public void makeEmpty() {
         doClear( );
     }

     private void doClear() {
         occupied = 0;
         for(int i = 0; i < array.length; i++) array[i] = null;
     }

     private int myhash(AnyType x) {
         int hashVal = x.hashCode();

         hashVal %= array.length;
         if(hashVal < 0) hashVal += array.length;

         return hashVal;
     }

     private static class HashEntry<AnyType> {
         public AnyType  element;
         public boolean isActive;

         public HashEntry(AnyType e) {
             this(e, true);
         }

         public HashEntry(AnyType e, boolean i) {
             element = e;
             isActive = i;
         }
     }

     private static final int DEFAULT_TABLE_SIZE = 101;

     private HashEntry<AnyType>[] array;
     private int occupied;
     private int theSize;

     private void allocateArray(int arraySize) {
         array = new HashEntry[nextPrime(arraySize)];
     }

     private static int nextPrime(int n) {
         if(n % 2 == 0) n++;

         for(; !isPrime(n); n += 2) ;

         return n;
     }

     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;
     }
 }

Map Skeleton From Textbook

class Map<KeyType,ValueType> {
    public Map()

    public void put(KeyType key, ValueType val)
    public ValueType get(KeyType key)
    public boolean isEmpty()
    public void makeEmpty()

    private QuadraticProbingHashTable<Entry<KeyType,ValueType>> items;

    private static class Entry<KeyType,ValueType> {
        KeyType key;
        ValueType value;
    }
}
chatblanc
  • 1
  • 1

1 Answers1

0

Generally, what you're facing is a problem of implementing a given interface. The Map is the interface - the HashTable is a means of implementing it, the underlying data structure.

However, I understand your confusion as the definition of the HashTable that you were provided seems ill-suited for the job as it does not seem to have an option to use a custom key (instead always relying on the object's hash code for calculating the hash) nor does it have an option to have a custom HashEntry. As the question is specified, I would say the answer is "you can't". Generally, implementing a Map on a HashTable comes down to handling collisions - one approach, which is not very effective but usually works, is that whenever you find a collision (a case where you have differing keys but the same hashes), you rehash the entire table until the collision is no longer there. The more commonly adopted answer is having a multi-level hashtable, which basically recursively stores a hashtable (calculating a different hash function) on each level. Another method is having a hashtable of arrays - where the arrays themselves store lists of elements with the same hash - and rehashing if the number of collisions is too large. Unfortunately, neither of those solutions is directly implementable with the sample class that you were provided. Without further context, I cannot really say more, but it just seems like a badly designed exercise (this is coming from someone who does occasionally torture students with similar things).

An way of hacking this within your framework is creating a Pair type whose hashCode function just calculates key.hashCode(). This way, as a value you could store an array (and then use the array approach I mentioned above) or you could store a single element (and then use the rehash approach). In either solution, solving the collision handling is the most difficult element (you have to handle cases where the HashTable contains() your Pair, but the value part of the pair doesn't equals() the element that you want to insert.

Piotr Wilkin
  • 3,446
  • 10
  • 18