覆盖hashcode()时会发生什么

时间:2014-11-18 05:46:57

标签: java hashcode

我有一些关于哈希码的困境。据我所知,Object类hashcode表示任何对象的内存位置,对于每个对象(不同的对象),这个hashcode都是唯一的。好的,这一点非常直截了当。

但是在散列(HashSet,HashMap等)的情况下,我们需要覆盖hashcode和equals方法。现在,在这种情况下,哈希码将表示存储桶位置。现在我几乎没有问题:

例如我正在学习Student类,我重写了Student类的hashcode()方法。

class Student{
   //
   int id;
   @override
   int hashcode(){
      return id*31;
   }
}

Student stu1 = new Student(1);
Student stu2 = new Student(1);

如果我创建两个返回类似哈希码的相同Student对象(stu1,stu2)。因此,如果我们在hashset中添加这两个对象,它将调用它的hashcode并获取存储桶位置,但真实学生(我们创建的两个类似对象)对象会发生什么?意味着它在堆中的内存位置是什么?两者都指向相同的堆位置吗?

4 个答案:

答案 0 :(得分:0)

HashCode永远不会确定堆中的位置。 equals和hashcode都用于比较对象的相等性,主要是在基于哈希的集合中,例如Hashtable和HashMap。

(绝对没有办法找出内存位置。)

答案 1 :(得分:0)

如您所知Set实施equals()hashcode()方法,

  1. 因此,如果两个对象的hashcode不同,那么集合中将有两个元素
  2. 如果Hashcode相等,则第二个条目将替换第一个条目。所以集合中只有一个元素
  3. 当两个不相等的对象具有相同的哈希值时,这会导致哈希表中的冲突,因为两个objects都希望位于同一个槽(桶)中。哈希算法必须解析此类collisions

    Student stu1 = new Student(1);
    Student stu2 = new Student(1);
    

    stu1stu2添加到set中具有相同值的集合中时,

    Set<Student> sset=new HashSet<Student >();    
    sset.add(stu1);
    sset.add(stu2);
    

    集合sset将包含两个具有相同值的值,但两者都引用两个不同的对象,因为它们的hashcode不同

答案 2 :(得分:0)

嗯......不难看出它们是否共享相同的内存位置。参考相等测试,即stu1 == stu2将回答这个问题。至于哈希,它们将属于同一个桶,因为它们具有相同的哈希码。因此,在您尝试检索学生的情况下,将使用equals方法来确定正在查找哪个学生。运行以下代码。

import java.util.HashSet;

class Student {
    //
    int id;

    Student(int id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return id * 31;
    }

    public static void main(String[] args) {
        Student stu1 = new Student(1);
        Student stu2 = new Student(1);
        Student stu3 = stu1;
        HashSet<Student> hash = new HashSet<Student>();
        hash.add(stu1);
        hash.add(stu2);

        if(stu1==stu2){
            System.out.println("Stu1 and stu2 refer to the same 
location in memory...");
        }

        if(stu1==stu3){
            System.out.println("Stu1 and stu3 refer to the same 
location in memory...");
        }

    }

}

答案 3 :(得分:0)

对象的Hashcode不是位于堆中的对象的物理内存位置。它将存储在类实例中的数据摘要转换为单个散列值(32位有符号整数)。 (来源wiki

But in case of hashing(HashSet, HashMap etc), we need to override hashcode and equals method

是的,除非您在任何地图实现的类中使用您的类实例作为键。

现在考虑一下你给出的场景:

@override
int hashcode(){
  return id*31;
}

什么都不会发生,但最终会在地图或集合中添加不同的对象。

重要合同:

对象hashcodeequals方法之间存在一个合约,即当您覆盖equals时,必须覆盖hascode(),反之亦然,以便均匀分布对象水桶。

小心!!

// The worst possible legal hash function - never use!

@Override 
public int hashCode() { 
return 42; 
}

(Source Effective Java)

这非常合法,因为它确保了相等的对象具有相同的哈希码。这很糟糕,因为它确保每个对象都具有相同的哈希码。因此,每个对象都会散列到同一个存储桶,并且散列表会退化为链接列表。应该以线性时间运行的程序改为以二次方运行。对于大型哈希表,这是工作和不工作之间的区别。

为了您的澄清:

考虑到这一点,Bellow是我的班级,在这里我定义了equals和hascode之间的契约,说明两个具有相同名称和名称的对象必须相同。

public class MyClass {
  private int roll;
  private String name;
  private String subject;

public MyClass(int roll, String name, String subject) {
    this.roll = roll;
    this.name = name;
    this.subject = subject;
}

@Override
public boolean equals(Object object) {
    boolean result = false;
    if (object == null || object.getClass() != getClass()) {
        result = false;
    } else {
        MyClass myclass = (MyClass) object;
        if (this.roll == myclass.getRoll()
                && this.name == myclass.getName()) {
            result = true;
        }
    }
    return result;
}

@Override
public int hashCode() {
    int hash = 17;
    hash = 31 * this.roll;
    hash = 31 * hash + this.name.hashCode();
    return hash;
}

public static void main(String args[]) {
    MyClass obj1 = new MyClass(1, "A", "Math");
    MyClass obj2 = new MyClass(1, "A", "Phy");
    MyClass obj3 = new MyClass(1, "B", "Chem");
    System.out.println("obj1.equals(obj2) : "+obj1.equals(obj2));  // true. As both the objects have the same roll & name
    System.out.println("obj1 == obj2 : "+(obj1 == obj2));   // false. because two are references of different instance.
    Set<MyClass> set = new HashSet<MyClass>();
    set.add(obj1);
    set.add(obj2);
    System.out.println("set :"+set.size()); // 1 object 
    for(MyClass cls:set){
    System.out.println(cls); //i.e [1 A Math]. It'll not replaced by the 2nd one.
    }
    Map<MyClass,String> map= new HashMap<MyClass,String>();
    map.put(obj1,"IN");
    map.put(obj2,"US");
    System.out.println("map :"+map.size());// 1 object

    for (Map.Entry<MyClass, String> entry : map.entrySet()){
        System.out.println(entry.getKey() + " : " + entry.getValue());   // [1 A Math : US]. here you may notice the key remains same but the value will be replaced.
    }

}

public int getRoll() {
    return roll;
}

public String getName() {
    return name;
}

public String getSubject() {
    return subject;
}
@Override
public String toString(){
    return ""+roll+" "+name+" "+subject;
}

}

注意:每当您使用new关键字创建对象时,每次它都会在堆中创建不同的对象,无论它们中的内容是否相同。