带有ArrayList通配符的Java HashMap

时间:2011-07-06 21:20:16

标签: java generics arraylist hashmap wildcard

我有一个HashMap,其值是ArrayLists,我正在尝试编写一个函数来接受这些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)

错误消息是

  

foo(HashMap<?,ArrayList<?>>)类型中的方法Example不适用   对于参数(HashMap<String,ArrayList<Integer>>)。

为什么我需要为extends ArrayList设置通配符?

我认为通过ArrayList<?>(没有? extends),我可以将函数限制为只有带有ArrayList值的HashMaps。

我也知道以下通用方法有效:

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

我认为ArrayList<?>的作用如何。有人能解释这里的细微之处吗?

3 个答案:

答案 0 :(得分:6)

通配符语法是故意设计的(误)引导人们相信它匹配任何类型。这适用于简单的情况

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

但请记住,它只适用于第一级,而不是更深层次

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

这很好

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

因为新的第1级?。 如果ST的子类型,则G<S>G<? extends T>的子类型。如果是S=List<String>T=List<?>

,请应用此选项

答案 1 :(得分:1)

  1. 类型HashMap<?, ArrayList<?>>表示地图,它将某些未知(固定)类型的键映射到列表,每个列表都包含某些未知固定类型的元素

  2. 这种地图的具体实例将是,例如:

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

    这是一个地图,其中我们可以添加任意ArrayLists 作为值,对于我们从中获取的值(列表),我们对参数类型一无所知。

  3. 类型HashMap<String, ArrayList<Integer>>表示从字符串到整数列表的映射。这是一个地图,我们只能将整数的ArrayLists作为值添加,不是任意的ArraysLists 。因此,它不能是类型2的子类型(因为它允许少于它)。

    (类型1是超类型2,也可以使密钥类型未知。)

  4. 类型HashMap<String, ? extends ArrayList<?>>表示从字符串到某种未知类型的映射,只知道它是ArrayList<?> 的子类型。在这样的映射中,我们不能插入任何东西(作为值),但是我们得到的所有值都保证是ArrayList<?>类型,即未知事物的arraylist(可能是每个列表的另一个东西) )。我们可以插入新的String键,但只能使用null值。

    类型2和类型3都是类型4的子类型(因为ArrayList<?>ArrayList<Integer>都满足extends ArrayList<?>条件),但彼此不符合。

  5. HashMap<?, ? extends ArrayList<?>>就像4,但我们也不知道密钥类型。这意味着我们无法对地图做任何事情,除了迭代它(或查找键的值 - get()采用Object参数,而不是K)并删除内容。对于我们检索的值,我们只能迭代和删除东西,而不是插入甚至重新排序任何东西。 (即类型4是类型5的子类型。)

  6. 类型为变量HashMap<K, ArrayList<V>>的方法参数类型<K, V>表示从某种类型K到某些类型V的列表的映射,其中K和V由调用者决定方法。类型3显然适合这种情况(使用<String, Integer>),因此您可以调用此方法。您无法将新密钥放入地图中,因为您的方法不知道K是什么,但您可以将现有密钥映射到新值。您可以使用new ArrayList<V>()(并将这些元素作为值放入地图中),但您只能将现有列表中的现有V对象放入这些列表中。比5型还要多得多。


  7. 请注意:您应该使用更常规的类型Map<K,V>List<E>而不是HashMap<K,V>ArrayList<E>作为您的方法,因为大多数情况下该方法都会适用于任何类型的地图/列表,并不关心具体实施。你也可以使用

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

    声明并初始化您的变量。 (您仍然可以将ArrayList<Integer>个对象作为值。)

答案 2 :(得分:0)

这很简单。即使HashMap<K, V>HashMap<K, W>的子类型,V也不是W的子类型。 (你已经知道了,对吗?)在这里,VArrayList<Integer>WArrayList<?>。 (重要的是它们是不同的。)是的,ArrayList<Integer>ArrayList<?>的子类型,但这就是我们之前讨论的内容,即它是无关紧要的。但是,由于通配符,HashMap<K, V> 是<{1}}的子类型。