4

I wrote a method that gets a random value from a Map. I first got all the values in the map and use a Random object to get a random one. Here is the method:

public static <K, V> V getRandomValInMap (Map<K, V> map) {
    Collection<V> values = map.values ();
    V[] array = (V[])values.toArray ();
    return array[random.nextInt (array.length)];
}

In the expression

(V[])values.toArray ()

Android Studio said that it is an "unchecked cast from Object[] to V[]". So I guess this is kind of "risky". I tried using another overload of toArray:

V[] array = new V[values.size()];
array = values.toArray (array);

And I guess this is not how you would use the overload because there's a compiler error saying that I cannot initialize V.

So I think maybe I should use the first method. But how do I "check" a cast to cancel the warning?

Or, you can tell me how to use the toArray(T[] array) overload correctly.

Sweeper
  • 145,870
  • 17
  • 129
  • 225

5 Answers5

2

You cannot create an array of a generic type, this is a known limitation in Java and there are good reasons about it. So this is not possible: V[] array = new V[values.size()];

See also:

What's the reason I can't create generic array types in Java?

Community
  • 1
  • 1
peter.petrov
  • 34,474
  • 11
  • 63
  • 118
1

Better aproach would be not to create new array at all:

public static <K, V> V getRandomValInMap (Map<K, V> map) {
    Collection<V> values = map.values ();

    Iterator<V> it = values.iterator();
    int choosen = new Random().nextInt (values.size());
    for (int i=0; i<values.size();i++) {
        V value = it.next();
        if (i==choosen) return value;
    }
    throw new AssertionError("Choosen value out of bounds");
}
Krzysztof Cichocki
  • 5,658
  • 1
  • 13
  • 31
0

You could check it in runtime, using instanceof, but if you know, that only correct type of objects will be in the map, you should just leave it as it is - fixing a warning with unnecessary runtime overhead is a bad idea. This warning basically means, that there is no way to be sure that you always get correct objects from the map.

The new V does not work because of type erasure. This was a design decision in Java to keep backwards compatibility, when generics was introduced.

If you only care about the warning, you can suppress it of course.

meskobalazs
  • 14,510
  • 2
  • 33
  • 55
0

There is a type erasure in Java, The generics type will be removed when compile and deal it as Object type.

You can suppress warning by:

public static <K, V> V getRandomValInMap (Map<K, V> map) {
    Collection<V> values = map.values ();
    @SuppressWarnings("unchecked") V[] array = (V[])values.toArray ();
    return array[1];
}
chengpohi
  • 13,467
  • 1
  • 21
  • 40
0

Quoting Bruce Eckel in Thinking in Java using Array.newInstance is the recommended approach to create arrays in generics.

public class ArrayMaker<T> { 
 private Class<T> kind; 
 public ArrayMaker(Class<T> kind) { this.kind = kind; } 
 @SuppressWarnings("unchecked") 
 T[] create(int size) { 
  return (T[])Array.newInstance(kind, size); 
 }
}

You can use this approach and populate your array from keys and values of your Map.

m c
  • 1,064
  • 1
  • 12
  • 20