HashMap
заменяет старое значение новым, когда вы вставляете его с тем же ключом.
Пример:
Map<Integer, String> map = new HashMap<>();
map.put(1, "first put");
System.out.println(map.get(1)); // <-- prints `first put`
map.put(1, "second put");
System.out.println(map.get(1)); // <-- prints `second put`
Таким образом, в основном, когда новое значение прибывает с ключом, уже присутствующим на карте, оно просто заменяет значение для этого ключа, потому что даже если ключ тот же, новое значение может отличаться от значения, связанного с этим ключом в карта.
В случае HashSet
есть только ключ , значение отсутствует. Поэтому, когда прибывает уже существующий key
, делать это не нужно, он уже есть.
Что касается внутренней реализации HashSet
, на самом деле он использует HashMap
внутри, но для этого он просто создает один object
:
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
и когда вам нужно add
некоторые key
для установки, он просто вызывает put(key, present)
на этой карте, используя один и тот же объект для всех записей (чтобы не выделять много бесполезных объектов:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Таким образом, в целом HashSet
использует внутренне HashMap
, и когда вы помещаете что-то в хэш-карту, всегда присваивается новое значение вместо старого, однако key
нет, и как HashSet
использует свои ключи в качестве ключей в HashMap, тогда, если вы вставите дублирующий ключ в HashSet
, он не удалит старый.
Пример: * +1032 *
Предположим, у нас есть некоторый класс сущностей, который имеет 2 поля: int id
и строку name
, но только id
участвует в реализации equals
метода.
Entity entity1 = new Entity(1, "some name");
Entity entity2 = new Entity(1, "some other name");
System.out.println(entity1.equals(entity2)); // returns true
Map<Integer, Entity> map = new HashMap<>();
map.put(entity1.id, entity1);
map.put(entity2.id, entity2);
System.out.println(map.get(entity1.id).name); // returns "some other name"
Map<Entity, String> keyMap = new HashMap<>();
keyMap.put(entity1, entity1.name);
keyMap.put(entity2, entity2.name);
System.out.println(keyMap.keySet()); // returns [Entity{id=1, name='some name'}]
System.out.println(keyMap.values()); // returns [some other name]
Set<Entity> set = new HashSet<>();
set.add(entity1);
set.add(entity2);
System.out.println(new ArrayList<>(set)); // returns [Entity{id=1, name='some name'}]
Также на это также не следует полагаться, поскольку его внутренние детали реализации, которые могут быть изменены в будущих выпусках, HashMap
и HashSet
используют equals
, проверяют, является ли объект тем же. Если у вас есть два объекта, которые equals
возвращают true
, но вы все равно хотите относиться к ним по-разному, скорее всего, в вашем дизайне сущности есть какая-то проблема, однако бывают редкие случаи, когда inded дает желаемый эффект, в таких случаях вы можно взглянуть на IdentityHashMap
.