java面试题:hashMap为什么要重写equals,hashcode方法

本文详细解析了hashCode和equals在Java中的作用与实现原理,强调了在HashMap、HashSet等集合中重写这两个方法的重要性,以及如何正确重写以提高程序效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

重写equals,hashcode原理详解

个人认为已经讲得很清楚了!希望大家有问题多多指教,评论即可!

Object中的原生方法:

Object中:

  • hashcode():返回的是对象的地址,所以这种情况下不同对象的hashcode肯定不同
  • equals():比较的是对象的地址

问题 hashmap,String,hashset中的hashcode和equals方法到底是怎么重写的!

知识扩展:(非常重要!!!!)

  • 1.hashmap和hashset中都是不允许元素重复的,那么我们每次加元素进去的时候,都要进行比较,是否元素重复了

  • 2.那么我们是否可以用Object里的equals方法来判断元素是否相同呢?答案是不行的,因为Object中的equals方法是判断对象地址的,如果是两个不同的对象,但是里面的内容相同,通过object中的equals方法同样返回的是不等,那么还会造成重复添加元素的问题,所以这里我们就要有一个结论,必须重写equals方法!(如何重写下面会讲到,无非就是判断内容是否相等)

  • 3.在我们重写equals方法后,我们又会发现一个问题。当集合中的数据量过大的时候,我们每次添加元素都要调用成千上万次的equals方法,那么这就使我们的代码效率非常低,所以这就是为什么我们要改写hashcode的原因。

  • 4.我们刚刚讲到Object中的hashcode方法是直接返回对象地址的,也就是说这种hashcode方法也无法根据对象的内容生成散列值(也就是方法返回的值),所以我们要改写Object中的hashcode方法,使其根据对象的内容生成一个散列值,每次插入一个新的对象,都要生成一个散列值,将其插入一个table中,那么这个table有什么用呢?

  • 5.实际上hashmap,hashset中判断元素是否重复是有两个过程的,首先生成插入新对象的hashcode,到我们刚刚寻找的table中去寻找(这种查询方式很快,用了哈希表的原理),当我们发现没有不同的散列值的时候,就可以判断这是一个不重复的元素,就可以直接插入了。当我们发现table已经有相同的散列值的时候,!!!并不是可以直接判断此对象就是重复元素了,还要调用equals方法,再判断一次,如果判断的结果还是真,那么就真的是重复元素了,就不存了,如果equals判断为假,那么就重新散列其他的值,再进行插入判断。

问题:为什么hashcode方法判断完为重复了,还要让equals再判断一次!

答:实际上hashcode方法(重写之后)的原理仅仅是根据对象内容去返回一串定长的数字,但是当数据量很大的时候,总会出现不同内容生成同样散列值的情况,因为这串数字是定长的!所以这就是关键了。

总结:

  • hashcode相同,元素对象不一定重复
  • hashcode不同,元素对象一定不重复
  • euqals判断是最精确的,用hashcode做第一步的初步判断是为了提高程序效率

String类中的hashcode和equals改写:

//string中的hashcode方法
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}
//string类中的equals方法
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
  • String类中的hashcode:根据字符串内容生成的一串数字,也就是说,一般情况下(这里就是指不出现不同元素相同散列值的情况)只要字符串的内容相等,那么这两个String对象的散列值就相同。
  • String类的equals:是去判断两个字符串每一个字符是否相等,最精准的判断

向hashset中添加String对象实例:

String str1 = new String("hello");
String str2 = new String("hello");
String str3 = new String("world");


System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true
System.out.println(str1.hashCode());//99162322
System.out.println(str1.hashCode());//99162322

Set hashset = new HashSet();
hashset.add(str1);
hashset.add(str2);
hashset.add(str3);
System.out.println(hashset);//[world, hello]

我们可以看到,事实上str1和str2的hashcode是一样的,但是hashset并没有重复添加这连个元素,就是因为散列值一样之后还要进行equals方法的判断!

向hashset中添加不重写hashcode,equals的对象:

这里我们自定义对象DataString,里面没有进行重写hashcode,equals方法

DataString dataString1 = new DataString("hello",20);
DataString dataString2 = new DataString("hello",20);
DataString dataString3 = new DataString("world",21);

System.out.println(dataString1.hashCode());//460141958
System.out.println(dataString2.hashCode());//1163157884
System.out.println(dataString3.hashCode());//1956725890

Set hashset1 = new HashSet();
hashset1.add(dataString1);
hashset1.add(dataString2);
hashset1.add(dataString3);
System.out.println(hashset1);
//DataString{name='world', age=21}, 
//DataString{name='hello', age=20}, 
//DataString{name='hello', age=20}

这里我们又可以看到,由于没有重写hashcode方法,哪怕内容相同的两个对象,hashcode也是不同的,所以我们可以添加重复对象,而实际上hashset是不允许元素重复的!所以我们要重写hashcode方法和euqals方法!

重写方法:

public int hashCode(){
    return age*name.hashCode();
}

public boolean equals(Object o) {
    DataString s = (DataString) o;
	return age == s.age && name.equals(s.name);
}

当我们重写方法之后再运行刚刚的程序:

结果:[DataString{name=‘hello’, age=20}, DataString{name=‘world’, age=21}]

所以到这基本上就可以明白了!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值