Руководство по Java IdentityHashMap

В этом уроке мы подробно изучим Java IdentityHashMap и различия между IdentityHashMap и HashMap.

IdentityHashMap использует равенство ссылок, а не равенство объектов при сравнении ключа(или значений). Другими словами, в IdentityHashMap два ключа k1 и k2 считаются равными тогда и только тогда, когда(k1==k2). Мы можем использовать изменяемые ключи в IdentityHashMap, поскольку равенство ссылок не меняется с состоянием объекта.

1. Введение в IdentityHashMap

Класс IdentityHashMap(присутствует в пакете java.util) представляет собой реализацию интерфейса Map на основе HashTable и присутствует с версии Java 1.4.

  • Этот класс не является реализацией Map общего назначения. Несмотря на то, что этот класс реализует интерфейс Map, он нарушает общий контракт Map на использование метода equals() при сравнении объектов. Он использует ссылочное равенство(==) для поиска ключей в карте. Этот класс используется только там, где требуется ссылочное равенство.
  • IdentityHashMap внутренне использует метод System.identityHashCode() для вычисления
  • IdentityHashMap имеет почти те же функции, что и HashMap, включая конструкторы и методы. Тем не менее, что касается производительности, она обеспечивает лучшую производительность по сравнению с HashMap, поскольку использует технику линейного зондирования HashTable по сравнению с техникой цепочек, используемой HashMap.
  • Его итераторы выдают исключение ConcurrentModificationException при попытке изменить карту во время итерации.
  • Это не потокобезопасный класс. Используйте Collections.synchronizedMap(), чтобы получить потокобезопасную ссылку на этот класс.

ВJava Collections класс объявлен следующим образом:

public class IdentityHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Serializable, Cloneable

Как показано выше, он реализует интерфейс Map и расширяет класс AbstractMap.

IdentityHashMap

2. Работа с IdentityHashMap

2.1 Создание IdentityHashMap

Мы можем создать IdentityHashMap, используя следующие конструкторы:

  • IdentityHashMap(): используется для создания пустой карты с начальной емкостью по умолчанию 21.
  • IdentityHashMap(int initialCapacity): используется для создания пустой карты с заданной начальной емкостью.
  • IdentityHashMap(Map m): используется для создания нового IdentityHashMap с теми же записями, что и указанная карта.
IdentityHashMap<String, String> map = new IdentityHashMap<>();IdentityHashMap<String, String> map = new IdentityHashMap<>(16);Map<String, String> map = new HashMap<String, String>() {{put("key1", "value1");put("key2", "value2");}};IdentityHashMap<String, String> map = new IdentityHashMap<>(map);

2.2 Методы IdentityHashMap

Некоторые из часто используемых методов, представленных в этом классе:

  • Объект put(ключ, значение): вставляет пару ключ-значение в карту.
  • Object get(key): возвращает значение для указанного ключа в карте.
  • boolean containsKey(key): возвращает значение true или false в зависимости от того, найден ли указанный ключ в карте или нет.
  • boolean containsValue(value): Подобно методу containsKey(), он ищет указанное значение вместо ключа.
  • Set keySet(): возвращает набор всех ключей, хранящихся в карте.
  • Set entrySet(): возвращает набор всех сопоставлений, сохраненных в карте.
  • Value remove(Object key): удаляет пару ключ-значение для указанного ключа.
  • int size(): возвращает размер карты, равный количеству пар ключ-значение, хранящихся в карте.

2.3 Пример IdentityHashMap

Давайте быстро рассмотрим пример создания его экземпляра и то, как можно использовать описанные выше методы.

//Creating IdentityHashMapIdentityHashMap<Integer, String> map = new IdentityHashMap<>();//Adding values to map using put()map.put(1, "A");map.put(2, "B");map.put(3, "C");System.out.println(map);//Getting a value from the mapString value = map.get(2);System.out.println(value);//Checking if a key or value present in the mapSystem.out.println(map.containsKey(3));System.out.println(map.containsValue("Z"));//Removing an entrymap.remove(3);System.out.println(map);//Finding map sizeSystem.out.println(map.size());//Iterating over the mapfor(Map.Entry<Integer, String> entry : map.entrySet()){System.out.println(entry.getKey() + " :: " + entry.getValue());}

Обратите внимание, что IdentityHashMap поддерживает пустые ключи и значения.

IdentityHashMap<String, String> map = new IdentityHashMap<>();map.put(null, "Some Value"); //Null keymap.put("Some Key", null); //Null value

3. Разница между HashMap и IdentityHashMap

3.1 Справочное равенство

IdentityHashMap использует ссылочное равенство(==) по сравнению с методом equals() Map при сравнении ключей(и значений). Давайте разберемся на примере.

// Two similar keys but different instances in memoryInteger key1 = new Integer(10);Integer key2 = new Integer(10);// Same keys in IdentityHashMapIdentityHashMap<Integer, String> identityHashMap = new IdentityHashMap<>();identityHashMap.put(key1, "India");identityHashMap.put(key2, "USA");System.out.println("Identity HashMap : " + identityHashMap);// Same keys in HashMapHashMap<Integer, String> hashMap = new HashMap<>();hashMap.put(key1, "India");hashMap.put(key2, "USA");System.out.println("HashMap : " + hashMap);

Обратите внимание на вывод программы. HashMap отклонил второй ключ, потому что он сравнивает ключи с помощью метода equals(), и для обоих ключей значение равно 10. IdentityHashMap использует равенство ссылок, и оба ключа хранятся в памяти отдельно, поэтому их ссылки будут неравными.

Когда мы помещаем пару ключ-значение в HashMap, она обновляет предыдущую запись, и мы получаем одну запись, сохраненную в Map.

Identity HashMap : {10=USA, 10=India}HashMap : {10=USA}

3.2 Изменяемые ключи

В IdentityHashMap можно использовать изменяемые ключи, тогда как для HashMap всегда рекомендуется использовать неизменяемые ключи.

Давайте разберемся на примере и создадим изменяемый класс 'Vehicle'. Определим необходимые методы доступа, методы hashCode() и equals().

class Vehicle {private String name;private int year;public Vehicle(String name, int year) {this.name = name;this.year = year;}//Getters and Setters@Overridepublic String toString() {return "Vehicle{" +"vehicleName='" + name + '\'' +", modelYear=" + year +'}';}@Overridepublic boolean equals(Object o) {if(this == o) return true;if(o == null || getClass() != o.getClass()) return false;Vehicle vehicle =(Vehicle) o;if(Objects.equals(year, vehicle.year)) return false;return Objects.equals(name, vehicle.name);}@Overridepublic int hashCode() {int result = name != null ? name.hashCode() : 0;result = 31 * result + year;return result;}}

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

Vehicle vehicle = new Vehicle("Honda", 2015);Map<Vehicle, String> identityHashMap1 = new IdentityHashMap<>();identityHashMap1.put(vehicle, "Old Vehicle");// Changing key statevehicle.setName("Modified Vehicle");vehicle.setYear(2022);// Getting value for key vehicle from the mapSystem.out.println( identityHashMap1.get(vehicle) ); //Prints 'Modified Vehicle'

4. Варианты использования IdentityHashMap

IdentityHashMap используется в редких случаях, и при использовании этого класса следует соблюдать осторожность.

Он помогает создавать определенные структуры, в том числе:

  • Spring-бины или типы Singleton, поскольку они управляют только одним экземпляром определенных типов
  • Поддержка прокси-объектов для набора изменяемых объектов
  • Объекты классов также сравнимы по ссылке.
  • Кэшированные экземпляры на основе ссылки на объект
  • Хранение в памяти графа объектов со ссылками

5. Заключение

Мы узнали о IdentityHashMap в Java, его внутренней работе и чем он отличается от HashMap. Мы также рассмотрели практические примеры, включающие как HashMap, так и IdentityHashMap, и как они ведут себя по-разному относительно сравнения ключей.

Исходный код на Github

Прокрутить вверх