0

I am trying to implement a basic hashmap in Java and am stuck on why I cannot declare an array of my custom class KVPair. I am getting an error after numerous trials of fixing my declaration of the array in my constructor:

contains = new KVPair[capacity];

When I tried this, I got a compile error saying that I "cannot create a generic array of HashMap.KVPair."

I have also seen from another stackexchange answer that suggested casting an array of objects into something else like this:

contains = (KVPair[])new Object[capacity];

When I do this, I get a run-time error saying "java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [MyHashMap$KVPair;."

Below I have included one of the constructors of my hashmap class as well as my KVPair class. Any help on how I can solve this issue would be much appreciated.

public class MyHashMap<K, V> implements Iterable<K> {

private static final int DEFAULT_CAPACITY = 200;
private static final double DEFAULT_LOAD_FACTOR = 0.7;

private int capacity; // the number of buckets in the map
private int size; // the number of items that have been put into the map
private double loadFactor;

KVPair[] contains;

// Constructs an empty map.
public MyHashMap() {
    capacity = DEFAULT_CAPACITY;
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    contains = (KVPair[]) new Object[capacity];
}

... 

public class KVPair {
    private K key;
    private V value;
    private KVPair next;
    private int hash;
    private KVPair(Object k, Object v){
        key = (K) k;
        value = (V) v;
        next = null;
        hash = k.hashCode();
    }
    public KVPair(Object k, Object v, KVPair nextKV){
        key = (K) k;
        value = (V) v;
        next = nextKV;
        hash = k.hashCode();
    }

}
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
Richard
  • 89
  • 1
  • 4
  • 1
    Well, you've got the syntax wrong, you're using the raw type to cast. However that alone won't solve the problem. Java's generics aren't refiable, and the runtime can't actually verify the cast. You'll have to use `@SupressWarnings`: http://stackoverflow.com/questions/1129795/what-is-suppresswarnings-unchecked-in-java – markspace Aug 19 '16 at 23:05
  • @markspace nothing at compile-time can cause a cast exception. It's not about syntax – Dici Aug 19 '16 at 23:07
  • @markspace what do you mean I am getting the syntax wrong? How else should I go about casting my Object array into a KVPair array? – Richard Aug 19 '16 at 23:24
  • Why are you passing in `k` and `v` to `KVPair`'s constructors as `Object`s, and then casting them? Why not just make them `K` and `V` in the first place, for type safety? – Andy Turner Aug 19 '16 at 23:35

1 Answers1

0

Usually implementations of collections that require an underlying generic array solve this problem by using a non-generic array of type Object[]. As long as the array is hidden as an implementation detail and that the method exposed by the collection are all generic, then it is completely type-safe to do so. You can see that in the JDK code in particular (for example: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ArrayList.java#ArrayList).

Another solution for instantiating generic arrays is to use Array.newInstance. However, this requires to pass the class of the generic type, which you most likely don't want to impose on the user of your API. Just for the record, here is as this can be used to create a generic array factory method:

public class ArrayUtils {
    @SuppressWarnings("unchecked")
    public static <T> T[] ofDim(Class<T> clazz, int length, T defaultValue) {
        T[] arr = (T[]) Array.newInstance(clazz, length);
        for (int i = 0; i < length; i++) arr[i] = defaultValue;
        return arr;
    }
}

In short:

  • if your generic array can be hidden as an implementation detail, use a bare Object[]
  • if your generic array is going to be passed and potentially mutated by an external class, you need to ensure runtime type safety using Array.newInstance
Dici
  • 25,226
  • 7
  • 41
  • 82
  • thanks for your answer. In your first suggestion to use a bare Object[], do you mean that I should type cast it as KVPair[], or that I should just leave it as an object array? If I leave it as an object array, I can't find a way to iterate through the keys using an iterator since I can't reference the key of the KVPair since its treated as an object. – Richard Aug 19 '16 at 23:30
  • I pointed you to the implementation of `ArrayList` in the JDK. They use a plain `Object[]` and simply cast the elements when they read them back. As I said, it's only a valid approach if your array is invisible from the outside – Dici Aug 19 '16 at 23:49