Java HashMap with Wildcard ArrayList

I have a HashMap where the values ​​are ArrayLists, and I'm trying to write a function to accept shared instances of these HashMaps

HashMap<String, ArrayList<Integer>> myMap = new HashMap<String, ArrayList<Integer>>(); public static void foo(HashMap<?, ArrayList<?>> a) {} public static void bar(HashMap<?, ? extends ArrayList<?>> a) {} // Compilation Failure! foo(myMap); // This works, but why do I need ? extends ArrayList bar(myMap) 

Error message

The foo(HashMap<?,ArrayList<?>>) method foo(HashMap<?,ArrayList<?>>) in the Example type is not applicable for arguments ( HashMap<String,ArrayList<Integer>> ).

Why do I need to have a wildcard for ArrayList extensions?

I thought that having an ArrayList<?> (Without ? extends ), I could restrict the function to only HashMaps using ArrayList values.

I also know that the following general method works:

 public static <K,V> void printer(HashMap<K, ArrayList<V>> list) { } 

Which behaves the way I thought ArrayList<?> Will work. Can someone explain the subtleties here?

+4
source share
3 answers

Wildcard syntax has been deliberately designed to (incorrectly) encourage people to believe that it matches any type. This works for simple cases.

 List<?> <= List<String> // OK, right is subtype of left 

But remember that it only works at level 1, not deeper

 List<List<?> <= List<List<String>> // FAIL 

This is normal

 List<? extends List<?>> <= List<List<String>> 

because of the new 1st level ? . If S is a subtype of T , G<S> is a subtype of G<? extends T> G<? extends T> . Apply this in case of S=List<String> , T=List<?> .

+6
source
  • Type HashMap<?, ArrayList<?>> means a map that maps keys of some unknown (fixed) type to lists, each of which has elements of an unknown fixed type.

  • A specific instance of such a mapping would be, for example:

     new HashMap<String, ArrayList<?>>(); 

    This is a map in which we can add arbitrary ArrayLists as values, and for the values ​​(lists) we get from them, we don’t know anything about the type of parameter.

  • The type HashMap<String, ArrayList<Integer>> means mapping from strings to lists of integers. This is a map to which we can add ArrayLists of integers only as values, not arbitrary lists of arrays . Thus, it cannot be a subtype of type 2 (since it allows less than it).

    (Type 1 is supertype 2, which also allows an unknown unknown key type.)

  • Type HashMap<String, ? extends ArrayList<?>> HashMap<String, ? extends ArrayList<?>> means mapping from strings to an unknown type, which is known only by the ArrayList<?> subtype. On such a map, we cannot insert anything (as a value), but all the values ​​that we get from it are guaranteed to be of type ArrayList<?> , I.e. Arraialist of something unknown (which may be different for each list). We can insert new string keys, but only with a null value.

    Both types 2 and type 3 are subtypes of type 4 (since both ArrayList<?> And ArrayList<Integer> fulfill the condition extends ArrayList<?> ), But not from each other.

  • HashMap<?, ? extends ArrayList<?>> HashMap<?, ? extends ArrayList<?>> is like 4, but we also don't know the key type. This means that we cannot do anything with the map except iterating over it (or searching for values ​​for keys - get() accepts the Object argument, not K ) and deleting the files. For the values ​​we retrieve, we can only iterate and delete things, and not insert or even reorder. (Ie type 4 is a subtype of type 5.)

  • Method argument type HashMap<K, ArrayList<V>> with variables of type <K, V> means a map from some type K to lists of some type V, where K and V are determined by the calling object of the method. Type 3 explicitly matches this (with <String, Integer> ), so you are allowed to call this method. You cannot put new keys on the map, since your method does not know what K , but you can map existing keys to new values. You can use new ArrayList<V>() (and put these elements on the map as values), but you can only put existing V objects from existing lists into these lists. Still much more possible than with type 5.


Just note: for your methods you should use the more general types Map<K,V> and List<E> instead of HashMap<K,V> and ArrayList<E> , since, of course, the method will work on any type of map / list and doesn't care about a particular implementation. Would you also use

 Map<String, List<Integer>> myMap = new HashMap<String, List<Integer>>(); 

to declare and initialize your variable. (You can put ArrayList<Integer> objects as values.)

+1
source

It is very simple. HashMap<K, V> not a subtype of HashMap<K, W> , even if V is a subtype of W (You already know that, right?) Here V is ArrayList<Integer> , and W is ArrayList<?> . (The important thing is that they are different.) Yes, ArrayList<Integer> is a subtype of ArrayList<?> , But what we examined earlier, i.e. Irrelevant. However, HashMap<K, V> is a subtype of HashMap<K, ? extends W> HashMap<K, ? extends W> because of a wildcard.

0
source

All Articles