tags:

views:

71

answers:

5

I have this function:

 /**
 * Helper function that adds the values of b to the elements of a, treating
 * all keys that exist in b but not in a, as existent in a with value 0. NB:
 * It operates IN PLACE. 
 * @param a The {@link java.util.Map} which will hold the result
 * @param b The {@link java.util.Map} which will be added to a
 */
private static void sumMaps(Map<?, Integer> a, Map<?,Integer> b)
{
    for (Object key : b.keySet()) {
        Integer currentCount = a.get(key);
        a.put(key, currentCount == null ? b.get(key) : currentCount + b.get(key));
    }
}

However, NetBeans highlights "key" in the final line of for, and gives me this error:

method put in class java.util.Map<K,V> cannot be applied to given types  
required: capture #67 of?, java.lang.Integer  
found: java.lang.Object, int

(the int is not the problem because of Java unpacking, I tried using Integers too but it didn't work).

+5  A: 
John Kugelman
Even though it's valid - I think I'd prefer `<K>` over `<Key>`, `Key` looks to much like a class name but it is a type variable.
Andreas_D
Since you bring it up, I find it sort of puzzling that convention is to use names like `T`, `K`, and `V`. It seems to me that this is a C++ convention that should have been dropped, in the same way that Java dropped other forms of abbreviation in favor of fully spelled out words.
John Kugelman
John - it's probably because there's no difference in usage between generic parameters and "real" class names. Since the generic parameter usage is always lexically local, it's usually fine to give them single-letter names and this functions well as a naming convention that distinguishes them from actual classes. When I look at your (correct) code snippet, my brain naturally interprets `Key` as an actual class whereas it would interpret `K` as a generic parameter.
Andrzej Doyle
+2  A: 

For ? read "some particular type" and note that this could be a different type each time you mention ?. So, for example, a could be a Map<String, Integer> and b could be a Map<BigDecimal, Integer>. So, indeed, you shouldn't be allowed to take a key out of a and put it into b and the compiler is stopping you. The compiler isn't very helpful in explaining this!

As other answers have suggested, you could make the method generic.

jjujuma
+3  A: 

? is not Object or something. It's totally unknown. Okay, I'm not that good at explaining that stuff either but there seems to be a very simple solution: Since the keys of both Maps need to be of the same type anyway why not introduce a generic type variable?

private static <T> void sumMaps(Map<T, Integer> a, Map<T, Integer> b) {
    for (T key : b.keySet()) {
        Integer currentCount = a.get(key);
        a.put(key, currentCount == null ? b.get(key) : currentCount
                + b.get(key));
    }
}
musiKk
+1  A: 

Object is not a valid substitute for ?. Here is a working version with a type variable T:

private static <T> void sumMaps(final Map<T, Integer> a,
    final Map<T, Integer> b){
    for(final T key : b.keySet()){
        final Integer currentCount = a.get(key);
        a.put(key,
            currentCount == null
                ? b.get(key)
                : Integer.valueOf(currentCount.intValue()
                    + b.get(key).intValue()));
    }
}

(I also removed some auto-boxing / unboxing)

seanizer
Why would you remove auto-boxing? I used it on purpose.
FrontierPsycho
Because I belong to the group of people who believes that autoboxing is an awful feature that causes many expected bugs (I always set my eclipse warning level to warning or error for autoboxing).
seanizer
+2  A: 

Specifying "?" for the key means that it could be of any type, but the keys must be the same for each Map for this method to work. So use the following instead:

private static <K> void sumMaps(Map<K, Integer> a, Map<K, Integer> b) {
        for (K key : b.keySet()) {
            Integer currentCount = a.get(key);
            a.put(key, currentCount == null ? b.get(key) : currentCount + b.get(key));
        }
    }
S73417H
Thank you very much! I had tried a generic type, but I got a "class not found: K" error. I didn't know I could "declare" the type before void. This was a very simple question after all.
FrontierPsycho
You can make that slightly more general. The first map key can be a base type of the second map key.
Tom Hawtin - tackline