Java 当对象Hashcode更改时,Hashmap或Hashset中的查找会发生什么变化
在Hashmap中,所提供键的哈希代码用于将值放入哈希表中。在Hashset中,obects hashcode用于将值放置在基础hashtable中。i、 hashmap的优点是,您可以灵活地决定想要什么作为密钥,这样您就可以做这样的好事Java 当对象Hashcode更改时,Hashmap或Hashset中的查找会发生什么变化,java,hash,hashmap,hashcode,hashset,Java,Hash,Hashmap,Hashcode,Hashset,在Hashmap中,所提供键的哈希代码用于将值放入哈希表中。在Hashset中,obects hashcode用于将值放置在基础hashtable中。i、 hashmap的优点是,您可以灵活地决定想要什么作为密钥,这样您就可以做这样的好事 Map<String,Player> players = new HashMap<String,Player>(); Map players=newhashmap(); 这可以将一个字符串(如球员姓名)映射到球员本身 我的问题是,当
Map<String,Player> players = new HashMap<String,Player>();
Map players=newhashmap();
这可以将一个字符串(如球员姓名)映射到球员本身
我的问题是,当键的哈希代码更改时,查找会发生什么变化
对于Hashmap来说,这并不是一个主要问题,因为我既不希望也不希望更改密钥。在上一个例子中,如果球员的名字改变了,他就不再是那个球员了。但是,我可以使用键“更改其他非名称字段”查找球员,以后的查找将起作用
但是,在Hashset中,由于整个对象的hashcode用于放置项,因此如果有人稍微更改对象,则该对象的未来查找将不再解析为Hashtable中的相同位置,因为它依赖于整个对象的hashcode。这是否意味着一旦数据位于哈希集中,就不应该更改它。还是需要重新洗刷?或者是自动完成的?发生了什么?哈希集由
哈希映射备份
从javadocs
此类实现了Set接口,该接口由哈希表支持
(实际上是一个HashMap实例)
因此,如果更改哈希代码,我怀疑您是否可以访问该对象
内部实施细节
add
实现了HashSet
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
键是元素,值只是一个称为PRESENT的虚拟对象
而包含的实现是
public boolean contains(Object o) {
return map.containsKey(o);
}
在您的示例中,键是不可变的字符串。所以密钥的哈希代码不会改变。当密钥的hashcode发生变化时会发生什么,这是未定义的,并且会导致“奇怪”的行为。请参见下面的示例,其中打印1、false和2。该对象仍保留在集合中,但集合看起来像已断开(contains返回false)
摘录自:
注意:如果将可变对象用作集合元素,则必须非常小心。当对象是集合中的元素时,如果对象的值以影响相等比较的方式更改,则不会指定集合的行为。这种禁止的一种特殊情况是,不允许集合本身包含为元素
publicstaticvoidmain(字符串参数[]){
Set=newhashset();
MyObject o1=新的MyObject(1);
添加(o1);
o1.i=2;
System.out.println(set.size());//1
System.out.println(set.contains(o1));//false
用于(MyObject o:set){
System.out.println(o.i);//2
}
}
私有静态类MyObject{
私人互联网i;
公共对象(int i){
这个。i=i;
}
@凌驾
公共int hashCode(){
返回i;
}
@凌驾
公共布尔等于(对象obj){
if(obj==null)返回false;
如果(getClass()!=obj.getClass())返回false;
最终MyObject other=(MyObject)obj;
如果(this.i!=other.i)返回false;
返回true;
}
}
使用Java的哈希,根本找不到原始引用。它在与当前哈希代码对应的bucket中搜索,但未找到
为了在事后恢复,必须迭代哈希键集,并且必须通过迭代器删除contains
方法未找到的任何键。最好是从映射中删除键,然后使用新键存储值。在您的示例中,字符串是不可变的,因此其哈希代码不能更改。但是假设,如果一个对象的hashcode在作为哈希表中的一个键时发生了变化,那么就哈希表查找而言,它可能会消失。我在对一个相关问题的回答中更详细地介绍了:。(最初的问题是关于HashSet
,但是HashSet
实际上是一个隐藏的HashMap
,因此答案也涵盖了这种情况。)
可以肯定地说,如果HashMap或TreeMap的键以影响它们各自的hashcode()
/equals(Object)
或compare(…)
或compareTo(…)
收缩的方式发生变异,那么数据结构将“中断”
这是否意味着,一旦数据位于哈希集中,就不应该对其进行更改
对
还是需要重新洗刷?或者是自动完成的
它不会自动重新灰化。HashMap
不会注意到键的hashcode已更改。实际上,当HashMap
调整大小时,您甚至不会重新计算hashcode。数据结构会记住原始哈希代码值,以避免在哈希表调整大小时必须重新计算所有哈希代码
如果知道某个键的hashcode将要更改,则需要在更改该键之前从表中删除该项,然后再将其添加回表中。(如果在更改密钥后尝试删除
/放置
,则删除
很可能找不到条目。)
发生了什么事
发生的事情是你违反了合同。不要那样做
合同由两部分组成:
在for对象
中指定的标准hashcode/equals契约
当对象是哈希表中的键时,对象的哈希代码不得更改的附加约束
后一个约束没有在HashMap
中明确说明,但是forMap
中说明:
注意:如果使用可变对象,必须非常小心
public static void main(String args[]) {
Set<MyObject> set = new HashSet<>();
MyObject o1 = new MyObject(1);
set.add(o1);
o1.i = 2;
System.out.println(set.size()); //1
System.out.println(set.contains(o1)); //false
for (MyObject o : set) {
System.out.println(o.i); //2
}
}
private static class MyObject {
private int i;
public MyObject(int i) {
this.i = i;
}
@Override
public int hashCode() {
return i;
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
final MyObject other = (MyObject) obj;
if (this.i != other.i) return false;
return true;
}
}