Java HashMap с массивом Int

Я использую этот код, чтобы проверить, что массив присутствует в HashMap:

public class Test {
    public static void main(String[] arg) {
        HashMap<int[], String> map = new HashMap<int[], String>();
        map.put(new int[]{1, 2}, "sun");
        System.out.println(map.containsKey((new int[]{1, 2})));
    }
}

Но это печатает False. Как я могу проверить, что массив присутствует в HashMap?

Ответы

Ответ 1

Проблема в том, что два int[] не равны.

System.out.println(
    (new int[] { 1, 2 }).equals(new int[] { 1, 2 })
); // prints "false"

Map и другие классы Java Collections Framework определяют его интерфейс с точки зрения equals. Из Map API:

Многие методы в интерфейсах Collections Framework определены в терминах метода equals. Например, спецификация для метода containsKey(Object key) гласит: "возвращает true том и только в том случае, если эта карта содержит отображение для ключа k такое, что (key==null? k==null: key.equals(k)). "

Обратите внимание, что они не должны быть одним и тем же объектом; они просто должны быть equals. Массивы в Java расширяются от Object, чья реализация equals умолчанию возвращает true только для идентификатора объекта; следовательно, почему он печатает false в приведенном выше фрагменте.


Вы можете решить свою проблему одним из многих способов:

  • Определите свой собственный класс-оболочку для массивов, для которых equals использует метод java.util.Arrays equals/deepEquals.
    • И не забывайте, что когда вы @Override equals(Object), вы также должны @Override hashCode
  • Используйте что-то вроде List<Integer> которое определяет equals в терминах значений, которые они содержат
  • Или, если вы можете работать с равенством ссылок для equals, вы можете просто придерживаться того, что у вас есть. Точно так же, как вы не должны ожидать, что приведенный выше фрагмент будет выводить true, вы не должны ожидать, что сможете найти ваши массивы только по его значениям; Вы должны держаться и использовать оригинальные ссылки каждый раз.

Смотрите также:

API

  • Object.equals и Object.hashCode
    • Для программиста на Java важно знать эти контракты и то, как заставить их работать с/для остальной системы

Ответ 2

Вы сравниваете две разностные ссылки - обратите внимание на двойное использование new. Что-то вроде этого будет работать:

public class Test {
    public static void main(String[] arg)
    {
     HashMap<int[],String> map= new HashMap<int[],String>();
     int[] a = new int[]{1,2};
     map.put(a, "sun");
     System.out.println(map.containsKey(a));
    }
}

Так как a является той же ссылкой, вы получите true, как ожидалось. Если ваше приложение не имеет возможности передавать ссылки для сравнения, я бы создал новый тип объекта, который содержит int[] и переопределяет метод equals() (не забудьте одновременно переопределить hashCode()), так что будет отображаться в вызове containsKey().

Ответ 3

Я бы использовал другой подход. Как упоминалось ранее, проблема заключается в равенстве массивов, которое основано на эталонном равенстве и делает вашу карту бесполезной для ваших нужд. Другая потенциальная проблема, предполагающая, что вы используете ArrayList, - это проблема согласованности: если вы измените список после добавления на карту, у вас будет повреждение hashmap, поскольку позиция списка больше не будет отражать его хэш-код.

Чтобы решить эти две проблемы, я бы использовал какой-то неизменный список. Например, вы можете создать неизменяемую оболочку для массива int и реализовать equals() и hashCode() самостоятельно.

Ответ 4

Я думаю, проблема в том, что ваш массив выполняет сравнение "==", то есть проверяет ссылку. Когда вы делаете containsKey (новый int [] {...}), он создает новый объект и, следовательно, эта ссылка не является одинаковой.

Если вы измените тип массива на нечто вроде ArrayList<Integer>, которое должно работать, однако я бы старался избегать использования списков в качестве ключей карты, поскольку это не будет очень эффективным.

Ответ 5

Реализация hashCode() для массивов выводится из Object.hashCode(), поэтому она зависит от расположения памяти массива. Поскольку два массива создаются отдельно, они имеют разные ячейки памяти и, следовательно, разные хэш-коды. Если вы сделали один массив, он будет работать:

int[] arr = {1, 2};
map.put(arr, "sun");
System.out.println(map.containsKey(arr));

Ответ 6

У вас есть два разных объекта, которые содержат одни и те же значения, потому что вы дважды вызывали новый код.

Один из подходов, который вы можете использовать, - создать собственный класс "владелец" и определить его равные и хэш-методы.

Ответ 7

Вы уверены, что не хотите отображать Strings в массивы, а не наоборот?

В любом случае, чтобы ответить на ваш вопрос, проблема в том, что вы создаете массив new при вызове containsKey(). Это возвращает false между вами, у вас есть два отдельно new ed массива, которые имеют одинаковые элементы и измерение. См. Ответ Yuval, чтобы увидеть правильный способ проверки, содержится ли массив в качестве ключа.

Альтернативный более продвинутый подход заключается в создании собственного класса, который обертывает массив и перезаписывает hashCode(), так что два массива с одинаковыми размерами и элементами будут иметь одинаковые хэш-коды.