1

I'm building a generic HashTable class in which we use the simply Java .hashCode() method to convert a key to hash, modulo by the array size, and store in the index that results. The only problem is that the professor wants us to store not only the value but also the key that we hashed. To do this, I created a Data class that stores and gets both values:

private class Data {
    private K key;
    private V value;

    public Data(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

Pretty simple stuff. The difficulty comes in when I attempt to create the array of Data items to hold the hashed values and keys that made them. K and V are generic values that were specified in the HashTable class declaration:

public class HashTable<K,V> implements Table<K,V>

When I first created the class, I simply used a cast on an object array to create an array of V's, but since we need to store both values, I decided to make the Data class and store them. But when I try it with my

hashArray = (Data[])new Object[arraySize];

It gives me the error

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Lproject3.HashTable$Data;

This leaves me unable to create an array of type Data. Is there another way that I could store this without using a List (as that is a parameter of the assignment)?

Archibald
  • 414
  • 1
  • 7
  • 20

1 Answers1

4

An Object[] is not a Data[]. The casting is bound to fail at runtime. But since the type is already known there, why don't you directly create a Data[]? There is no point creating an Object[] and attempt to cast it.

Now to answer your other question, the below casting:

V[] arr = (V[]) new Object[arraySize];

works, because the type of V is not known at runtime. The type information is erased. And since the erasure of a type parameter is it's left-most bound, which is in this case Object, the casting above at runtime looks like this:

Object[] arr = (Object[]) new Object[arraySize];

Seems fine, doesn't it? However, this way will also fail as soon as you let the array escape your class, and assign the return value to any reference like this:

HashTable<String, Integer> map = new HashTable<String, Integer>();
// suppose you have a getter to get the array stored
Integer[] arr = map.getValueArray();

The 2nd line above will throw a ClassCastException at runtime for the same reason as posted above. So, casting an Object[] to V[] will work, only till you don't let the array escape the class.

Now, consider another case, where you give a bound to V, say V extends Comparable<V>, and see what happens;

public class MyClass<T extends Comparable<T>> {
    T[] arr;

    public MyClass() {
        arr = (T[])new Object[0];
    }
}

In this case, the erasure of type parameter T is Comparable, so the cast in the constructor is erased to:

arr = (Comparable[]) new Object[0];

And this will again throw a ClassCastException, whether or not you let the array escape the class.

So, the point is, you need to be very careful while creating an array of generic types.


See also:

Community
  • 1
  • 1
Rohit Jain
  • 195,192
  • 43
  • 369
  • 489