Recently on the interview I was asked the question:
What will happen if we have two equal objects and we put them as values using the same key? Will the first value be replaced or does hashmap uses
equals()
for values in order to determine whether or not the element already exists?
I answered that if element already present in the bucket than it won't be replaced nor duplicate element will be added.
However, I tried to code this and I see that it's not true. The old object will be replaced.
I have User
entity(randomId
is used to determine which object is currently in the HashMap):
class User {
private String userInfo;
private String randomId = UUID.randomUUID().toString();
public User(String userInfo) {
this.userInfo = userInfo;
}
public String getRandomId() {
return randomId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(userInfo, user.userInfo);
}
@Override
public int hashCode() {
return Objects.hash(userInfo);
}
}
Now I'm testing the code:
public class HashMapTest {
public static void main(String[] args) {
Map<String, User> userLogins = new HashMap<>();
User user1 = new User("regular user");
User user2 = new User("regular user");
System.out.println(user1.getRandomId());
System.out.println(user2.getRandomId());
userLogins.put("login", user1);
userLogins.put("login", user2);
for(Map.Entry<String, User> entry : userLogins.entrySet()) {
System.out.println(entry.getKey() + " | " + entry.getValue().getRandomId());
}
}
}
Output:
63d21f34-c58c-4c73-8ee3-975951faf491
9b493ee7-33f2-4f93-92ae-8b44cba6e7c3
login | 9b493ee7-33f2-4f93-92ae-8b44cba6e7c3
Can anyone explain why hashmap replaces old value? I thought that hashcode & equals
contract implies that there shouldn't be difference between user1
and user2
objects. So, from my point of view, it's logical to check if an equal object already exists in the bucket and don't replace it.
答案 0 :(得分:4)
Because Map.put()
javadoc states that :
If the map previously contained a mapping for the key, the old value is replaced by the specified value.
So put()
doesn't matter of the actual value for an existing key, just it overwrites it with the new value.
I thought that hashcode & equals contract implies that there shouldn't be difference between user1 and user2 objects.
Yes but the javadoc of Map.put()
doesn't say that it checks the equality of the value for existing mapping before effectively putting the value.
It is your guesswork.
答案 1 :(得分:4)
HashMap<K,V>
uses hashCode
and equals
only on the key objects. It never compares its value objects, treating them as a data load.
This behavior is specified in the documentation:
public V put(K key, V value)
Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced.
Note the return type of put
. It's not void
, it's V
, because when a value gets replaced with a new value, you get the old one back:
User old1 = userLogins.put("login", user1);
User old2 = userLogins.put("login", user2);
Above, old1
would be null
, while old2
would be user1
:
System.out.println(old2.getRandomId());
答案 2 :(得分:1)
The documentation for put
says:
Associates the specified value with the specified key in this map (optional operation). If the map previously contained a mapping for the key, the old value is replaced by the specified value. (A map m is said to contain a mapping for a key k if and only if m.containsKey(k) would return true.)
(my emphasis)
Note that it says nothing about checking whether the value equals anything, nor should it. The coder has expressly called put
, saying "Put this value in the map under this key." It's not for the map to second-guess the programmer writing that code. It would also be unnecessary overhead.
There can be all kinds of reasons a coder wants to have a specific object, not just an equivalent one, in the map. Canonical caching (à la String#intern
) is probably the first that comes to mind.
答案 3 :(得分:1)
If you did it this way, there's no confusion; so why would a HashMap
(or any Map
) be different?
User user1 = new User("regular user");
User user2 = new User("regular user");
User login = user1;
login = user2;
You used the exact same key each time. There can only be one value for a key in a HashMap
.
The javadoc for Map.put() says that the method:
Returns: the previous value associated with key, or null if there was no mapping for key.
So actually, you could write this:
userLogins.put("login", user1);
User oldvalue = userLogins.put("login", user2);
assert(oldvalue == user1);
答案 4 :(得分:0)
Map designed to work with key-value basis. Meaning that, at any point of time, key have only one value and if you keep adding the value with same key, that value gets overridden for the provided key.
Nothing to do with the value's. Just key
is the thing here.
You just used the same key twice and earlier one replaced with new value. That is what exactly how map should work.
答案 5 :(得分:0)
在问为什么时,应该考虑其他选择。我看到的替代方案是要么默默地忽略第二次放置或抛出异常。我建议实际行为要好得多。
问题的基本假设是错误的:
我认为哈希码&amp;等于合同意味着不应该 是user1和user2对象之间的区别。
hashcode
而非equals
都不要求对象相同。 hashcode contract具体不排除具有相同散列的不同对象:
不是必需的 如果两个对象根据不相等而不相等 equals(java.lang.Object)方法,然后调用hashCode方法 两个对象中的每一个都必须产生不同的整数结果。
关于equals
,示例中的对象不相等,但即使equals
不比较userInfo,使它们相等,equals
也是有效的。< / p>