【发布时间】:2019-10-09 07:29:28
【问题描述】:
我有一个类 Employee 有两个属性。
public class Employee {
private int empId;
private String empName;
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Employee)) return false;
Employee employee = (Employee) o;
return getEmpId() == employee.getEmpId() &&
Objects.equals(getEmpName(), employee.getEmpName());
//return Objects.equals(getEmpName(), employee.getEmpName());
}
@Override
public int hashCode() {
//return Objects.hash(getEmpId());
return Objects.hash(getEmpId(), getEmpName());
}
}
我使用这个类作为 Hashmap 中的一个键。
现在,当我修改原始对象 emp 在这种情况下更改员工对象的名称时,我无法访问我最初保存在地图中的条目。只有当我将名称回滚到它的原始值时,我才能再次访问该对象。
这表明当我更改 Employee 对象中的名称时,它的哈希值已更改,并且它没有存储在 Hashmap 中的正确存储桶下。
Map<Employee, String> map = new HashMap<>();;
// Set Employee with Name Shashi
Employee emp = new Employee();
emp.setEmpId(1);
emp.setEmpName("Shashi");
// Add to Map
map.put(emp, emp.getEmpName());
// Modify The Original Employee object's Name
emp.setEmpName("Shashi Bhushan");
// This object does not exist as key in map now
Assert.assertFalse(map.containsKey(emp));
// Create object with same name(used when creating)
Employee similarEmployee = new Employee();
similarEmployee.setEmpId(1);
similarEmployee.setEmpName("Shashi");
// Hashcode check will pass, equals will fail
Assert.assertFalse(map.containsKey(similarEmployee));
Assert.assertNull(map.get(similarEmployee));
// Create object with same name(modified name)
Employee anotherSimilarEmployee = new Employee();
anotherSimilarEmployee.setEmpId(1);
anotherSimilarEmployee.setEmpName("Shashi Bhushan");
// Hashcode check will fail
Assert.assertFalse(map.containsKey(anotherSimilarEmployee));
Assert.assertNull(map.get(anotherSimilarEmployee));
// Now, if I roll back the name, i could again fetch using the new created object as well.
// Since now this new object will be equivalent to the old object.
emp.setEmpName("Shashi");
Assert.assertTrue(map.containsKey(similarEmployee));
Assert.assertNotNull(map.get(similarEmployee));
能够在地图中获取对象的一个解决方案是使 Employee 类不可变。
我能想到的另一个理论解决方案是重新散列地图并将修改后的员工对象保留在地图中正确的存储桶中,但我在 hashmap 中看不到任何重新散列它的方法。请建议我的想法是否正确,或者是否有其他解决方案。
附:所有这些都是为了理解hashmap,所以没有限制如何解决这个问题。
【问题讨论】:
-
这就是为什么你不应该在基于散列的结构中使用可变对象作为键。
-
对于哈希映射键,我认为您只有两个选择:1)使其不可变,2)不要覆盖 hashCode 和 equals,并使用原始对象实例进行查找
-
@boot-and-bonnet 的回答正是我想说的。如果您确实想更改密钥,唯一安全的方法是从哈希图中删除密钥,更改密钥,然后将其放回原处。
-
所以,如果我不覆盖 equals 和 hashcode,每个新的 Employee 实例都会不同,无论它是否具有相同的 id 和 name。到目前为止,在方法中仅使用 id 的建议(完全忽略名称)对我来说似乎是有效的。 :)
-
或者只使用 Integer id 作为映射键