hashCode()方法和equals()方法的作用其实一样,都是用来比较两个对象是否相等,既然equals()方法已经能实现对比的功能,为什么还要用hashCode()呢?
首先我们来看Object类
java.lang.Object类中有两个非常重要的方法:
public int hashCode(); public boolean equals(Object obj);Object类是所有类的父类,所有的对象,包括数组,都实现了在Object类中定义的方法
equals()方法使用来判断当前对象和其它对象是否相等
源码
public boolean equals(Object obj) { return (this == obj); }很明显是对两个对象的地址进行比较(引用是否相同)。但是,String、和包装类在使用equals()方法时,已经覆盖了 Object类的equals()方法。
我们用一个案例演示一下,
测试类
package com.blog.controller; import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { Person p1 = new Person("张三", 18); Person p2 = new Person("张三", 18); System.out.println("equals()比较:"+p1.equals(p2)); System.out.println("p1---hashCode:"+p1.hashCode()); System.out.println("p2---hashCode:"+p2.hashCode()); Map<Person, String> map = new HashMap<>(); map.put(p1, "zhangsan"); System.out.println("结果是:" + map.get(p2)); } }输出结果:
equals()比较:false p1---hashCode:1995265320 p2---hashCode:746292446 结果是:null执行main方法发现,运行equals()方法的执行结果为false;
分析: Pserson类没有覆盖equals()方法,p1调用的equals()方法实际上等同于调用Object类的equals()方法。比较的是对象的内存地址是否相等。因为两个新对象所以内存地址不同,则p1.equals(p2)
输出结果:
equals()比较:false p1---hashCode:24021577 p2---hashCode:24021577 结果是:null分析: hash码是age和name生成的,我们没有重写equals方法只重写了hashCode()方法,两个对象的hashCode值一样,但通过map.get(p2)获取值却null,因为在hashMap.get()方法中会进行equals()方法进行比较两个对象是否为false,所以就没有比较两个对象的hashCode值
##3.重写equals()方法不重写hashCode()方法时,让Person类通过判断对象内容是否相等类确定对象是否相等
public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person)o; return age == person.age && Objects.equals(name, person.name); } }输出结果:
equals()比较:true p1---hashCode:1995265320 p2---hashCode:746292446 结果是:null分析: 因为Person两个对象的age和name属性相等,而且重写了equals方法来判断的,所以p1.equals(p2)为true,注意 :map.get(p2)时,期望结果是"zhangsan"却为null
用equals比较对象相同,但是在hashMap中却以不同的对象存储(没有重写hashCode(),两个hashCode()值,在它看来就是两个对象) 到底两个对象相等不相等? 说明必须重写hashCode()的重要性
Person类中没有重写hashCode()方法,导致了两个相同的实例具有不相同的散列码(hashCode),违背了hashCode约定。 ##4.重写equals()和hashCode()
public class Person { private int age; private String name; public Person(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person)o; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + age; return result; } }输出结果:
equals()比较:true p1---hashCode:24021577 //相同的值 p2---hashCode:24021577 结果是:zhangsan //说明以一个值key存储,相同的值当map.get(p2)时,结果是"zhangsan"
这也就说明了重写equals()方法同时重写hashCode()方法,就是为了保证当这两个对象equals()方法比较相等时,那么hashCode值也一定要相等,equals为true,hashCode相等,所以在map.get(p2)时,结果是"zhangsan"
##5.那么我们就来做一个特别的例子吧,用hashSet添加对象,然后修改一下p2的属性值
public class Test { public static void main(String[] args) { Person p1 = new Person("张三", 18); Person p2 = new Person("张三", 18); System.out.println("equals()比较:"+p1.equals(p2)); System.out.println("p1---hashCode:"+p1.hashCode()); System.out.println("p2---hashCode:"+p2.hashCode()); Set<Person> set = new HashSet<>(); set.add(p1); set.add(p2); System.out.println("set.size():"+set.size()); p2.setAge(20); System.out.println("set.remove:"+set.remove(p2)); } }输出结果:
equals()比较:true p1---hashCode:24021577 p2---hashCode:24021577 set.size():1 set.remove:false分析: 在获取集合元素对象放入set中时,那么以后该对象的属性参与了hashCode的计算,伴儿以后就不能修改该对象参与的hashCode计算的那些属性了,否在可能会引起一些意想不到的错误
map.put源码
final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }##通过案例回到上面问题:equals()方法已经能实现对比的功能,为什么还要用hashCode()呢?
重写的equals方法里一般比较的比 全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只需要生成hash值进行比较就可以了,效率也很高,既然hashCode效率这么高,为哈还要重写equals()呢。
因为我们的hashCode()并不是完全可靠,有时候不同的对象生成的hashCode也会一样,所以说hashCode()方法大部分时候可靠,并不是绝对可靠,由此我们可以得出:
equals()相等对象他们的hashCode肯定相等,也就是equals比较是绝对可靠的hashCode()相等的两个对象他们的equals()不一定相等,也就是为什么说hashCode不是绝对可靠的。hashCode 只要在集合中用的到总结 1.为什么要重写equals呢?因为在java的集合中,通过equals来判断两个对象是否相等的 2.当集合元素过多时,或者是重写equals()方法比较复杂时,我们只用equals()方法进行比较判断,效率较低,所以引入hashCode这个方法,为了提高效率
我们也可以得出:
equals相等的两个对象他们的hashCode肯定是相等的,也就是equals去比较对象是绝对可靠的hashCode相等的对象他们呢的equals不一定相等,这也是hashCode不是绝对可靠的。换句话说equals不相等的对象hashCode有可能相等(可能是哈希码产生造成的冲突)