Java HashMap с международным массивом

Я использую этот код, чтобы проверить, что массив присутствует в 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?

16
задан Oleksandr Pyrohov 24 July 2019 в 18:55
поделиться

7 ответов

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

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

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

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

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


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

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

См. Также:

API

  • Object.equals и Object.hashCode
    • Java-программисту важно знать об этих контрактах и ​​о том, как заставить их работать с / для остальной системы
30
ответ дан 30 November 2019 в 15:47
поделиться

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

int[] arr = {1, 2};
map.put(arr, "sun");
System.out.println(map.containsKey(arr));
2
ответ дан 30 November 2019 в 15:47
поделиться

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

Один из подходов, который вы можете использовать, - это создать собственный класс-«держатель» и определить методы equals и hash этого класса.

1
ответ дан 30 November 2019 в 15:47
поделиться

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

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

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

1
ответ дан 30 November 2019 в 15:47
поделиться

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

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

2
ответ дан 30 November 2019 в 15:47
поделиться

Вы сравниваете две разные ссылки - обратите внимание на двойное использование 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().

8
ответ дан 30 November 2019 в 15:47
поделиться

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

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

4
ответ дан 30 November 2019 в 15:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: