代码如下所示:
public class Test2 { public static void main(String[] args) { Object t = new Object(); int a = t.hashCode(); System.out.println(Integer.toHexString(a)); // 输出 4554617c } }其中4554617c就是对象a的内存地址,这里转成16进制显示(是因为通常地址都是用16进制显示的,比如我们电脑的Mac地址)
下面总结下hashCode的几个特性:
一致性:无论hashCode调用多少次,都应该返回一样的结果(这一点跟equals很像)
跟随性(自己编的一个性):如果两个对象的equals返回为真,那么hashCode也应该相等
反过来,如果两个对象的equals返回为假,那么hashCode有可能相等,但是如果散列的足够好,那么通常来说hashCode()也不应该相等
覆写equals方法时,一定要覆写hashCode方法
equals和hashCode有什么联系呢?hashCode和equals可以说相辅相成的,他俩共同协作用来判断两个对象是否相等
如果分开来看的话,他俩是没什么联系的,但是由于某些原因导致被联系上了(比如HashMap这个小月老)
下面来细说一下
我们知道 HashMap集合中的key是不能重复的,那它是怎么判断重复的呢?
就是通过equals和hashCode来判断的
下面是部分源码
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e;可以看到,map先进行hash判断,然后进行equals判断
也就是说,hash是前提,如果hash都不相等,那equals就不用比较了(先计算hash的一个原因是计算hash比equals快得多)
所以我们在自定义对象时,如果覆写了equals,那么一定要记得覆写hashCode,(当然,假设这里的自定义对象是用来作为map中的key键的)
覆写代码如下:
@Override public boolean equals(Object o) { if (this == o) return true; if(getClass() != o.getClass()) return false; if(!super.equals(o)) return false; EqualsDemo demo = (EqualsDemo) o; return m == demo.m && Objects.equals(str,demo.str); } @Override public int hashCode() { return Objects.hash(m, str); }其中Objects.hash有点类似于上面的Objects.equals()方法,很实用
如果只覆写了equals,没有覆写hashCode,会咋样呢?
结果就是:
当你创建两个对象(属性一致,但是内存地址不一致),作为key放到map中时就会被当成两个key来存放
同理可得,获取数据value的时候,也是不一致的
下面是只覆写equals没覆写hashCode的代码:可以看到,两次取到的值是不一样的
public class HashCodeDemo{ public static void main(String[] args) { // 两个对象的属性都为n = 1 HashCodeDemo demo1 = new HashCodeDemo(1); HashCodeDemo demo2 = new HashCodeDemo(1); Map<HashCodeDemo, Integer> map = new HashMap<>(); map.put(demo1, 1); map.put(demo2, 2); System.out.println(map.get(demo1)); // 输出1 System.out.println(map.get(demo2)); // 输出2 } private int n; public HashCodeDemo(int n) { this.n = n; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HashCodeDemo that = (HashCodeDemo) o; return n == that.n; } public int getN() { return n; } public void setN(int n) { this.n = n; } }同时覆写equals和hashCode的代码:可以看到,两次取到的值都是一样的
public class HashCodeDemo{ public static void main(String[] args) { HashCodeDemo demo1 = new HashCodeDemo(1); HashCodeDemo demo2 = new HashCodeDemo(1); Map<HashCodeDemo, Integer> map = new HashMap<>(); map.put(demo1, 1); // 第二次会覆盖第一次的值,因为key相等(equals和hashCode都相等) map.put(demo2, 2); System.out.println(map.get(demo1)); // 输出2 System.out.println(map.get(demo2)); // 输出2 } private int n; public HashCodeDemo(int n) { this.n = n; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HashCodeDemo that = (HashCodeDemo) o; return n == that.n; } @Override public int hashCode() { return Objects.hash(m, str); } public int getN() { return n; } public void setN(int n) { this.n = n; } }HashSet集合也是同理,因为它内部的就是依赖HashMap实现的(这个前面有简单介绍过,感兴趣的可以回顾一下)
总结equals方法的特性:
自反性:就是自己反过来跟自己比,要返回true;比如x.equals(x) == true
对称性:就是x.equals(y) == true时,也要y.equals(y) == true
传递性:就是x.equals(y) == true,同时y.equals(z) == true,那么x.equals(z) == true
一致性:就是传说中的幂等性,即x.equals(y)调用多次,都应该返回一样的结果
非空和空比较则返回false的特性:就是x.equals(y)中,如果x非空,而y空,则返回false
hashCode的特性以及和equals的联系
一致性:无论hashCode调用多少次,都应该返回一样的结果(这一点跟equals很像)
跟随性(自己编的一个性):如果两个对象的equals返回为真,那么hashCode也应该相等
反过来,如果两个对象的equals返回为假,那么hashCode有可能相等,但是如果散列的足够好,那么通常来说hashCode()也不应该相等
覆写equals方法时,一定要覆写hashCode方法
后记最后,感谢大家的观看,谢谢