equals和hashCode是Object类中的两个方法。
这两个方法会被所有的类继承,也就是说所有的类都有这两个方法。
这两个方法可以被重写。
重写equals和hashCode有一个原则:
如果两个对象是equals的,那么尽量使hashCode相同;
如果两个对象的hashCode相同,那么这两个对象不一定equals。
最近有一个人问我一个问题。
给一个Person类追加了一个equals方法,但是没有重写hashCode方法有没有什么问题?
当然没有问题了,但是他说是有问题的。问题出在了HashMap这里。如果把这个Person对象作为key放到HashMap中,value暂且不管,会出问题。
以上是环境。尽管他说的不太对,但是有一定的启发。
HashMap存在的意义是能够方便的获取某一个key所对应的value。而通过这个key去获取对象的时候会调用这个key的hashCode方法。
public class HashEqualsTester { public static void main(String[] args) { Person p1 = new Person(); p1.setId("abc"); p1.setName("ABC"); p1.setAge(25); p1.setDescription("I am just person 1."); Person p2 = new Person(); p2.setId("abc"); p2.setName("ABC"); p2.setAge(25); p2.setDescription("I am just person 2."); Map<Person, String> map = new HashMap<Person, String>(); map.put(p1, p1.getDescription()); map.put(p2, p2.getDescription()); } } class Person { private String id; private String name; private Integer age; private String description; @Override public int hashCode() { System.out.println("hashCode called..."); return 1; } // get and set }
执行main函数,会打印:
hashCode called...
hashCode called...
但是我们打印map的时候,如:
System.out.println(map);
执行结果为:
hashCode called...
{hash.equals.Person@1=I am just person 2.}
并且,map中只存在一条记录。也就是说当两个对象的hashCode值相同的时候,只保存一个值。
如果hashCode不相同呢?
如果两个对象的hashCode相同,会继续调用对象的equals方法,如果两个对象是equals的,那么也只会在map中存一个值,并且是后一个值。
把代码放一下:
package hash.equals; import java.util.HashMap; import java.util.Map; public class HashEqualsTester { public static void main(String[] args) { Person p1 = new Person(); p1.setId("abc"); p1.setName("ABC"); p1.setAge(25); p1.setDescription("I am just person 1."); Person p2 = new Person(); p2.setId("abc"); p2.setName("ABC"); p2.setAge(25); p2.setDescription("I am just person 2."); Map<Person, String> map = new HashMap<Person, String>(); map.put(p1, p1.getDescription()); map.put(p2, p2.getDescription()); System.out.println(map); System.out.println(map.get(p1)); System.out.println(map.get(p2)); } } class Person { private String id; private String name; private Integer age; private String description; @Override public boolean equals(Object person) { System.out.println("equals called..."); return true; } @Override public int hashCode() { System.out.println("hashCode called..."); return 1; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
执行结果:
hashCode called... hashCode called... equals called... hashCode called... {hash.equals.Person@1=I am just person 2.} hashCode called... I am just person 2. hashCode called... equals called... I am just person 2.
话虽如此,这里面仍然有几个东西不太明确。
1,为什么要用一个对象作为map的key呢?
2,知道了这些有什么意义呢?
3,有一本java书籍上说过,除非你有很正当的理由,不然别去重写你的equals和hashCode方法。
=================================
* Note that it is generally necessary to override the {@code hashCode}
* method whenever this method is overridden, so as to maintain the
* general contract for the {@code hashCode} method, which states
* that equal objects must have equal hash codes.
这是java官方的equals中的一段描述,如果对equals方法进行了override的话,重写hashCode通常就是必须的了。
===============================================================
重写了equals方法,而不去重写hashcode并没有什么太大的问题,只不过会造成性能低下。