Java HashMap с подстановочными знаками ArrayList - PullRequest
4 голосов
/ 07 июля 2011

У меня есть 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>>).

Зачем мне нужен групповой символ для расширений ArrayList?

Я думал, что, имея ArrayList<?> (без ? extends), я мог бы ограничить функцию только HashMaps со значениями ArrayList.

Я также знаю, что работает следующий универсальный метод:

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

Что ведет себя так, как я думал ArrayList<?> будет работать. Может кто-нибудь объяснить здесь тонкости?

Ответы [ 3 ]

6 голосов
/ 07 июля 2011

Синтаксис с подстановочными знаками был специально разработан, чтобы (не) заставить людей поверить, что он соответствует любому типу.Это работает для простых случаев

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

Но помните, это работает только на 1-м уровне, а не глубже

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

Это нормально

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

из-зановый 1-й уровень ?.Если S является подтипом T, G<S> является подтипом G<? extends T>.Примените это в случае S=List<String>, T=List<?>.

1 голос
/ 07 июля 2011
  1. Тип HashMap<?, ArrayList<?>> означает около карту, которая отображает ключи некоторого неизвестного (фиксированного) типа в списки, каждый из которых имеет элементы некоторого неизвестного фиксированного типа .

  2. Конкретной реализацией такой карты будет, например:

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

    Это карта, в которой мы можем добавить произвольные ArrayLists какзначения, и для значений (списков) мы получаем из них, мы ничего не знаем о типе параметра.

  3. Тип HashMap<String, ArrayList<Integer>> означает отображение из строк всписки целых чисел .Это карта, в которую мы можем добавлять только ArrayLists of Integers в качестве значений, , а не произвольные ArraysLists .Таким образом, он не может быть подтипом типа 2 (поскольку он допускает меньше, чем он).

    (Тип 1 - это супертип 2, что также позволяет неизвестному типу ключа.)

  4. Тип HashMap<String, ? extends ArrayList<?>> означает карту из строк неизвестного типа, которая, как известно, является подтипом ArrayList<?>.В такую ​​карту мы не можем ничего вставить (как значение), но все значения, которые мы получаем из нее, гарантированно будут иметь тип ArrayList<?>, то есть массив неизвестных данных (которые могут быть другими для каждого списка).Мы можем вставлять новые строковые ключи, но только со значением 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.


Примечание: вы должны были использовать более общие типы Map<K,V> и List<E> вместо HashMap<K,V>и ArrayList<E> для ваших методов, поскольку, скорее всего, метод будет работать с любым типом карты / списка и не заботится о конкретной реализации.Вы также можете использовать

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

для объявления и инициализации вашей переменной.(Вы все еще можете поместить ArrayList<Integer> объекты в качестве значений.)

0 голосов
/ 07 июля 2011

Это очень просто. HashMap<K, V> не является подтипом HashMap<K, W>, даже если V является подтипом W. (Вы уже знаете это, верно?) Здесь V - это ArrayList<Integer>, а W - это ArrayList<?>. (Важно то, что они разные.) Да, ArrayList<Integer> - это подтип ArrayList<?>, но это то, что мы рассмотрели ранее, то есть это не имеет значения. Однако HashMap<K, V> является подтипом HashMap<K, ? extends W> из-за подстановочного знака.

...