Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/349.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 当对象Hashcode更改时,Hashmap或Hashset中的查找会发生什么变化_Java_Hash_Hashmap_Hashcode_Hashset - Fatal编程技术网

Java 当对象Hashcode更改时,Hashmap或Hashset中的查找会发生什么变化

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(); 这可以将一个字符串(如球员姓名)映射到球员本身 我的问题是,当

在Hashmap中,所提供键的哈希代码用于将值放入哈希表中。在Hashset中,obects hashcode用于将值放置在基础hashtable中。i、 hashmap的优点是,您可以灵活地决定想要什么作为密钥,这样您就可以做这样的好事

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
    中明确说明,但是for
    Map
    中说明:

    注意:如果使用可变对象,必须非常小心
    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;
        }
    }