3

I have this following method:

public static <T, U> T[] getKeysForValue(Map<T,U> map,U value){
    if(map == null || map.isEmpty()) {
        return null;
    }

    Set<T> keys = new HashSet<T>();

    for (Map.Entry<T,U> entry : map.entrySet()) {
        if (entry.getValue().equals(value)) {
            keys.add(entry.getKey());
        }
    }

    return keys.toArray(new T[keys.size()]);
}

I am getting compilation error on the line: keys.toArray(new T[keys.size()]), which says "Cannot create a generic array of T", which is obvious. How can I solve this issue?

Tapas Bose
  • 25,780
  • 71
  • 202
  • 317
  • 6
    @feralin A longer, actually useful answer would be better. Some of the apparent deficiencies in Java's generics system can be made to work by using explicit type tokens. Unfortunately those don't help when the type parameter is itself parameterised, but that's not the case here. Not knowing how to do this in Java isn't a good excuse for trolling. – millimoose Jul 11 '13 at 14:08
  • @feralin I have equal expertise in C# and Java, so it would be very much controversial to compare them. – Tapas Bose Jul 11 '13 at 14:11
  • @millimoose why do you say I'm trolling? Would you please provide some proof or evidence of that? – feralin Jul 11 '13 at 14:49
  • 2
    @feralin Your comment is at the very least a close neighbor to "the answer is your language sucks". Seeing as intent doesn't transfer over the internet I have no "proof" of it, but then again we're not in a court of law. Also, splitting hairs over this is ignoring the crux of my objection, which is that your comment wasn't very constructive. – millimoose Jul 11 '13 at 16:04

2 Answers2

5

Why not just return the Set? That seems to make more sense in this context anyway.

public static <T, U> Set<T> getKeysForValue(Map<T, U> map, U value) {
    ...

    return keys;
}

An alternative, if you still want to return an array, would be to pass an array to be filled as an argument:

public static <T, U> T[] getKeysForValue(Map<T,U> map, U value, T[] dest) {
    ...

    return keys.toArray(dest);
}
arshajii
  • 118,519
  • 22
  • 219
  • 270
4

You should pass the class corresponding to T as argument of your method and call Array.newInstance(clazz, size)

public static <T, U> T[] getKeysForValue(Class<T> clazz, Map<T,U> map,U value){

  T[] array = (T[])Array.newInstance(clazz, size);
Arnaud Denoyelle
  • 25,847
  • 10
  • 73
  • 128
  • Without passing clazz there is no way? By determining the class type of the key at runtime, can't I do that? – Tapas Bose Jul 11 '13 at 14:07
  • No, because of type erasure in generics, the type is not known at runtime so you have no chance to guess it. At runtime, `Map` basically becomes `Map`. – Arnaud Denoyelle Jul 11 '13 at 14:11
  • +1, There is other way, I did my own answer. I should have tried little more before. :-) – Tapas Bose Jul 11 '13 at 14:24
  • Might want an `@SuppressWarnings("unchecked")` – arshajii Jul 11 '13 at 14:43
  • 1
    not gonna work if the caller doesn't have the class of T either (T is a type variable in caller). we cannot pass the class through all APIs. the better answer is - don't use array, use collection. – ZhongYu Jul 11 '13 at 15:03