I am making a program that creates a generic hashtable that has both generic keys and generic data. When I cast the data array as the type Person, a class I created, and I try to access the data stored inside an individual Person from my Driver class using a getter method, I get an Object-type returned. My question is how do I access a non-generic class's information through a generic class, since it is stored in a generic array.
This is how I constructed the hashtable:
//region Instance Variables
private int count;
private K[] keys;
private E[] data;
private boolean[] hasBeenUsed;
//endregion
//region Constructor
public Table(int capacity)
{
if (capacity <= 0)
throw new IllegalArgumentException("Capacity is negative.");
keys = (K[]) new Object[capacity];
data = (E[]) new Object[capacity];
hasBeenUsed = new boolean[capacity];
}
My data getters(part of the Table
class):
public E[] getData()
{
return data;
}
public E getDataAt(int index)
{
return data[index];
}
This is where I am trying to access the information from my Driver class:
public void print(Table hash)
{
Person[] people = toArray(hash);
for (int i = 0; i < hash.getData().length; i++)
{
if (null == hash.getKeyAt(i))
System.out.println("NULL AT " + i);
else
System.out.println("Key: " + hash.getKeyAt(i) + " Data: " + hash.getDataAt(i));
}
}
private Person[] toArray(Table hash)
{
Person[] people = new Person[hash.getData().length];
for (int i = 0; i < hash.getData().length; i++)
{
people[i] = hash.getDataAt(i);
}
}
This is my entire Hashtable Class if it's needed:
public class Table<K,E>
{
//region Instance Variables
private int count;
private K[] keys;
private E[] data;
private boolean[] hasBeenUsed;
//endregion
//region Constructors
/**
* Constructor
* Instantiates the keys, data, and hasBeenUsed variables with a passed value of capacity
* @param capacity the size to give the three instance arrays
*/
@SuppressWarnings("unchecked")
public Table(int capacity)
{
if (capacity <= 0)
throw new IllegalArgumentException("Capacity is negative.");
keys = (K[]) new Object[capacity];
data = (E[]) new Object[capacity];
hasBeenUsed = new boolean[capacity];
}
/**
* Constructor
* Default-Sets arrays to size 10
*/
@SuppressWarnings("unchecked")
public Table()
{
keys = (K[]) new Object[10];
data = (E[]) new Object[10];
hasBeenUsed = new boolean[10];
}
//endregion
//region Public Methods
/**
* Put
* Adds a new set to the table
* @param key The new Key value
* @param data the new Data value
* @return null if this is a new set, the old data value if the key already exists
*/
public E put(K key, E data)
{
int index = findIndex(key);
E answer;
if (index != -1)
{
answer = (E) this.data[index];
this.data[index] = data;
return answer;
} else if (count < this.data.length)
{
index = hash(key);
while (keys[index] != null)
{
System.out.println("Collision!");
index = nextIndex(index, key);
}
keys[index] = key;
this.data[index] = data;
hasBeenUsed[index] = true;
count++;
return null;
} else
System.out.println("ERROR IN PUT");
return null;
}
/**
* Remove
* Removes a key-data set from the table
* @return the value removed
*/
@SuppressWarnings("unchecked")
public E remove(K key)
{
int index = findIndex(key);
E answer = null;
if (index != -1)
{
answer = (E) data[index];
keys[index] = null;
data[index] = null;
count--;
}
return answer;
}
/**
* Contains Key
* Checks if the passed key exists
* @param key generic type key to check for
* @return true if the key exists
*/
public boolean containsKey(K key)
{
for (int i = 0; i < data.length; i++)
{
if (hasBeenUsed[i])
{
if (keys[i].equals(key))
{
return true;
}
}
}
return false;
}
/**
* Get
* Retrieves the data held stored with key
* @param key the key to access
* @return the data at key, null if key does not exist
*/
@SuppressWarnings("unchecked")
public E get(K key)
{
int index = findIndex(key);
if (index == -1)
return null;
else
return (E) data[index];
}
//endregion
//region Private Methods
//Locates the index value of key
private int findIndex(K key)
{
int count = 0;
int i = hash(key);
while ((count < data.length) && (hasBeenUsed[i]))
{
if (key.equals(keys[i]))
return i;
count++;
i = nextIndex(i, key);
}
return -1;
}
//Hashes the key
private int hash(K key)
{
return Math.abs(key.hashCode()) % data.length;
}
private int hash2(K key)
{
return 1 + (Math.abs(key.hashCode()) % (data.length-2));
}
//Determines if the next index is valid
private int nextIndex(int i, K key)
{
return (i + hash2(key)) % data.length;
}
//endregion
//region Getters and Setters
public int getCount()
{
return count;
}
public void setCount(int count)
{
this.count = count;
}
public K[] getKeys()
{
return keys;
}
public K getKeyAt(int index)
{
return keys[index];
}
public void setKeys(K[] keys)
{
this.keys = keys;
}
public E[] getData()
{
return data;
}
public E getDataAt(int index)
{
return data[index];
}
public void setData(E[] data)
{
this.data = data;
}
//endregion
}
EDIT I edited the print method, and now I am getting a ClassCastException
Here is my new print
method:
public void print(Table<Integer, Person> hash)
{
for (int i = 0; i < hash.getKeys().length; i++)
{
if (null == hash.getKeyAt(i))
System.out.println("NULL AT " + hash.getKeyAt(i));
else
{
System.out.println("Data at key " + hash.getKeyAt(i) + ": \n");
hash.getDataAt(i).printInfo();
}
}
}
You
Table
is actually generic. So when you want a new Table instance, you can saynew Table<KEY_TYPE, VALUE_TYPE>()
which will not need you to cast anywhere.If you're not sure how to create an instance of generic class, please check https://docs.oracle.com/javase/tutorial/java/generics/types.html.