Java HashMap с массивом Int - PullRequest
       47

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

15 голосов
/ 13 апреля 2010

Я использую этот код для проверки наличия массива в 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})));
    }
}

Но это печатает Ложь. Как я могу проверить, что массив присутствует в HashMap. Заранее спасибо.

Ответы [ 7 ]

29 голосов
/ 13 апреля 2010

Проблема в том, что два 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-программист знал об этих контрактах и ​​о том, как заставить их работать с / для остальной системы
8 голосов
/ 13 апреля 2010

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

4 голосов
/ 13 апреля 2010

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

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

2 голосов
/ 13 апреля 2010

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

int[] arr = {1, 2};
map.put(arr, "sun");
System.out.println(map.containsKey(arr));
2 голосов
/ 13 апреля 2010

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

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

1 голос
/ 13 апреля 2010

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

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

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

1 голос
/ 13 апреля 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...