【问题标题】:Why does HashMap override the existing object when I am returing different hash codes for the same object?当我为同一个对象返回不同的哈希码时,为什么 HashMap 会覆盖现有对象?
【发布时间】:2019-10-31 10:59:52
【问题描述】:
import java.util.HashMap;
import java.util.Map; 

class Geek  
{ 

    public String name; 
    public int id; 

    Geek(String name, int id)  
    { 

        this.name = name; 
        this.id = id; 
    } 

    @Override
    public boolean equals(Object obj) 
    { 

    // checking if both the object references are  
    // referring to the same object. 
    if(this == obj) 
            return true; 

        // it checks if the argument is of the  
        // type Geek by comparing the classes  
        // of the passed argument and this object. 
        // if(!(obj instanceof Geek)) return false; ---> avoid. 
        if(obj == null || obj.getClass()!= this.getClass()) 
            return false; 

        // type casting of the argument.  
        Geek geek = (Geek) obj; 

        // comparing the state of argument with  
        // the state of 'this' Object. 
        System.out.println("equals method ....."+(geek.name == this.name && geek.id == this.id));
        return (geek.name == this.name && geek.id == this.id); 
    } 

    int counter = 0;

    @Override
    public int hashCode() 
    { 

        // We are returning the Geek_id  
        // as a hashcode value. 
        // we can also return some  
        // other calculated value or may 
        // be memory address of the  
        // Object on which it is invoked.  
        // it depends on how you implement  
        // hashCode() method. 
        ++counter;
        System.out.println("counter ::>>> "+counter);
        return counter;
    } 

驱动代码:

public static void main (String[] args) 
{ 

    Map<Geek, Integer> map = new HashMap<>();

    // creating the Objects of Geek class. 
    Geek g1 = new Geek("aa", 1); 
    Geek g2 = new Geek("aa", 1); 


    map.put(g1, g1.id);
    map.put(g2, g2.id);

    map.forEach((k,v) -> {
        System.out.println("key = "+k + "\n value = "+v);
    });

   /* else
    System.out.println("Both Objects are not equal. ");  */
}

在这里,我重写了hashCode() 方法,但地图仍然只包含一个对象g2。考虑到我的哈希码每次都返回不同的整数,为什么 HashMap 不存储两个对象?

即使我的equals() 方法对同一个对象返回true,为什么HashMap 没有存储两个对象?有人可以在这方面指导我吗?

【问题讨论】:

  • 虽然在这种情况下您的 hashCode 方法实际上确实为不同的对象返回相同的哈希码,但 HashMap 使用 equals 来比较不同哈希码的对象并非不可能,如果对象应该碰巧落在同一个哈希表桶中。您故意在此处尝试破坏地图,因此您不应该期望符合逻辑的行为。

标签: java java-8


【解决方案1】:

您的counter 变量是一个实例变量,因此对于每个Geek 实例,它都被初始化为0。因此,g1g2 在您将它们放入 1 时具有相同的 hashCode()1,并且被 HashMap 认为是相同的,因为它们基于您的equals 实现。

如果将counter 更改为static,则Geek 的2 个实例将得到不同的hashCode(),它们将存储在单独的映射条目中。

也就是说,您的hashCode() 实现非常糟糕。如果你多次为同一个实例调用hashCode(),你每次都会得到不同的结果!这意味着如果您尝试将g1 两次放入Map,它可能会放入两次,因为第二次put 将看到不同的hashCode(),因此将在不同的存储桶中搜索密钥.

【讨论】:

  • 你没有得到我的问题。试着理解我在问你什么。不要告诉我equals()和hashcode的合约。我很清楚。我想问的是 - 为什么 hashmap 没有存储 2 个对象?
  • @Vikasnaik 您是否打算在其他答案之一下发布此评论?我没有告诉你equals和hashcode的契约。
  • @Vikasnaik 正如我所解释的,hashCode()Geek 的两个实例返回1(第一次为这两个实例调用hashCode)。因此HashMap 将它们存储在同一个存储桶中,并且由于它们相等,因此第二个覆盖第一个。
  • @Vikasnaik 也许您应该将map.put(g1, g1.id); map.put(g2, g2.id); 替换为System.out.println(g1.hashCode()); System.out.println(g2.hashCode()); 以更好地理解我的答案。
  • @LewBloch 这不是保证行为。使用哈希码是一种性能优化,仅此而已。你需要保持 hashCode 和 equals 同步,这样不管它是否使用哈希码。
【解决方案2】:

hashCode() 函数在同一个对象实例上多次调用时不能改变。你不能每次调用它时都生成一个新值,现在你是通过递增计数器来实现的。

根据Object.hashCode() javadoc

只要在 Java 应用程序执行期间对同一个对象多次调用它,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上相等比较中使用的信息。该整数不需要在应用程序的一次执行与同一应用程序的另一次执行之间保持一致。

【讨论】:

  • 虽然这是正确的,但它并不能解释 OP 遇到的行为。
  • 你没有得到我的问题。试着理解我在问你什么。不要告诉我 equals() 和 hashcode 的合约。我很清楚。我想问的是 - 为什么 hashmap 没有存储 2 个对象?
猜你喜欢
  • 2013-03-26
  • 2015-08-12
  • 1970-01-01
  • 2014-02-13
  • 1970-01-01
  • 1970-01-01
  • 2015-01-13
  • 2012-06-27
  • 1970-01-01
相关资源
最近更新 更多