我有一些关于哈希码的困境。据我所知,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并获取存储桶位置,但真实学生(我们创建的两个类似对象)对象会发生什么?意味着它在堆中的内存位置是什么?两者都指向相同的堆位置吗?
答案 0 :(得分:0)
HashCode永远不会确定堆中的位置。 equals和hashcode都用于比较对象的相等性,主要是在基于哈希的集合中,例如Hashtable和HashMap。
(绝对没有办法找出内存位置。)
答案 1 :(得分:0)
如您所知Set
实施equals()
和hashcode()
方法,
hashcode
不同,那么集合中将有两个元素Hashcode
相等,则第二个条目将替换第一个条目。所以集合中只有一个元素当两个不相等的对象具有相同的哈希值时,这会导致哈希表中的冲突,因为两个objects
都希望位于同一个槽(桶)中。哈希算法必须解析此类collisions
。
Student stu1 = new Student(1);
Student stu2 = new Student(1);
将stu1
和stu2
添加到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;
}
什么都不会发生,但最终会在地图或集合中添加不同的对象。
对象hashcode
和equals
方法之间存在一个合约,即当您覆盖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
关键字创建对象时,每次它都会在堆中创建不同的对象,无论它们中的内容是否相同。