Java 当我为同一个对象返回不同的哈希代码时,为什么HashMap会覆盖现有对象?

Java 当我为同一个对象返回不同的哈希代码时,为什么HashMap会覆盖现有对象?,java,java-8,Java,Java 8,驱动程序代码: 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 boolea

驱动程序代码:

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;
    } 
publicstaticvoidmain(字符串[]args)
{ 
Map Map=newhashmap();
//创建Geek类的对象。
极客g1=新极客(“aa”,1);
极客g2=新极客(“aa”,1);
map.put(g1,g1.id);
map.put(g2,g2.id);
图.forEach((k,v)->{
System.out.println(“key=“+k+”\n value=“+v”);
});
/*否则
System.out.println(“两个对象不相等”)*/
}
在这里,我覆盖了
hashCode()
方法,但映射仍然只包含一个对象,即
g2
。既然我的hashcode每次返回一个不同的整数,为什么HashMap不存储两个对象呢


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

您的
计数器
变量是一个实例变量,因此每个
极客
实例都将其初始化为
0
。因此,当您将
g1
g2
放在
Map
中时,它们都具有与
1
相同的
hashCode()
,并且被
HashMap
视为相同,因为它们基于
equals
实现彼此相等

如果将
计数器
更改为
静态
,则
极客
的两个实例将获得不同的
hashCode()
,它们将存储在单独的地图条目中

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

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

根据:

在Java应用程序的执行过程中,每当在同一对象上多次调用hashCode方法时,只要没有修改对象上的equals比较中使用的信息,hashCode方法必须始终返回相同的整数。从应用程序的一次执行到同一应用程序的另一次执行,该整数不必保持一致


虽然这是正确的,但它不能解释OP遇到的行为。你没有明白我的问题。试着按照我的要求去理解它。不要告诉我equals()和hashcode的约定。我很清楚。我想问的是-为什么hashmap没有存储2个对象?你没有理解我的问题。试着按照我的要求去理解它。不要告诉我equals()和hashcode的约定。我很清楚。我想问的是-为什么hashmap没有存储2个对象?@Vikasnaik你打算在其他答案下发表这篇评论吗?我没有告诉你equals和hashcode的约定。@Vikasnaik正如我所解释的,
hashcode()
Geek
的两个实例返回
1
(第一次为这两个实例调用hashcode)。因此,
HashMap
将它们存储在同一个bucket中,由于它们相等,第二个将覆盖第一个。@Vikasnaik也许您应该替换
map.put(g1,g1.id);map.put(g2,g2.id)System.out.println(g1.hashCode())的code>;System.out.println(g2.hashCode())以更好地理解我的答案。@LewBloch这不是一个保证的行为。使用哈希代码是一种性能优化,仅此而已。您需要保持hashCode和equals同步,这样它是否使用哈希代码就无关紧要了。虽然在本例中,您的
hashCode
方法实际上会为不同的对象返回相同的哈希代码,如果对象恰好落在同一个哈希表存储桶中,
HashMap
不可能使用
equals
来比较不同哈希代码的对象。你故意试图破坏这里的地图,所以你不应该期望逻辑行为。
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. ");  */
}