为什么要重写hashCode() 和 equals()
equals()
默认的Object类里面equals()方法是根据对象所在的内存来做判断的,如果两个对象引用指向的是同一个内存,则返回true,但是,在某些场景一下,我们不想这么苛刻,比如是String类的equals(),只是判断了String.value的值,String其它的属性是不参与判断的,所以我们比较字符串的时候只是比较其中的内容,下面是String的equals()方法和hashCode()方法。
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; } 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; }
hashCode()
一般而言,默认的hashCode()方法会返回一个与对象的内存地址相关的值,有时候直接就是内存地址;所以,如何两个对象的hashCode()不相等,则它们肯定是不相等;但反过来,如果两个对象的hashCode()相等,它们不一定是相等的。
如果我们创建了属于自己的Class,还使用默认的hashCode()方法,那就可以出错,特别是在使用该Class作为HashMap的Key的时候。
重写hashCode()的基本原则
该基本原则是参考《Effective Java》Joshua Bloch's recipe
A、初始化一个整形变量,为此变量赋予一个非零的常数值,比如int result = 17;
B、选取equals方法中用于比较的所有域(之所以只选择equals()中使用的域,是为了保证上述原则的第1条),然后针对每个域的属性进行计算:
(1) boolean,c = f ? 1:0
(2) bytecharshortint, c= (int)f
(3) long,c = (int)(f ^ (f >>> 32))
(4) float,c = Float.floatToIntBits(f)
(5) double,long l = Double.doubleToLongBits(f),c = (int)(l ^ (l >>> 32))
(6) Object,对里面的属性采用上面同一的方法来判断
(7) 如果是数组,那么需要为每个元素当做单独的域来处理。java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上。
(8)、最后,把每个域的散列码合并到对象的哈希码中。
一个具体例子
public class Person { private String name; private byte[] password ; private String nickname ; // ignore the nickname private int age ; private double height ; private long birthYear ; private float sex ; private boolean alive; public Person(String name, byte[] password, int age, double height, long birthYear, float sex, boolean alive , String nickname) { this.name = name; this.password = password; this.nickname = nickname; this.age = age; this.height = height; this.birthYear = birthYear; this.sex = sex; this.alive = alive; } public String getName() { return name; } public void setName(String name) { this.name = name; } public byte[] getPassword() { return password; } public void setPassword(byte[] password) { this.password = password; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } public long getBirthYear() { return birthYear; } public void setBirthYear(long birthYear) { this.birthYear = birthYear; } public float getSex() { return sex; } public void setSex(float sex) { this.sex = sex; } public boolean isAlive() { return alive; } public void setAlive(boolean alive) { this.alive = alive; } /** * Joshua Bloch's recipe * @return int */ @Override public int hashCode() { int result = 17 ; result = 37 * result + name.hashCode(); result = 37 * result + Arrays.hashCode(password); result = 37 * result + age; long l = Double.doubleToLongBits(height); result = 37 * result + (int)(l ^ (l >>> 32)); result = 37 * result + (int)(birthYear ^ (birthYear >>> 32)); result = 37 * result + Float.floatToIntBits(sex); result = 37 * result + (alive ? 0 : 1 ); return result; } @Override public boolean equals(Object obj) { if (this == obj){ return true; } if (obj instanceof Person){ Person anotherPerson = (Person)obj; if (this.getName().equals(anotherPerson.getName()) && Arrays.equals(this.getPassword(),anotherPerson.getPassword()) && this.age == anotherPerson.getAge() && this.height == anotherPerson.getHeight() && this.birthYear == anotherPerson.getBirthYear() && this.sex == anotherPerson.getSex() && this.alive == anotherPerson.isAlive() ){ return true; }else { return false; } } return false; } } public class Main { public static void main(String[] args) { String name = "Tom"; byte [] password = "123".getBytes(); int age = 18 ; double height = 3.14; long birthYear = 2000 ; float sex = 1 ; boolean alive = true ; Person Tom = new Person(name,password,age,height,birthYear,sex,alive,"Tom"); Person Deal = new Person(name,password,13,height,birthYear,sex,alive,"Deal"); Person Jack = new Person(name,password,age,height,birthYear,sex,alive,"Jack"); // map是根据hashcode来判断的,然后还有疑惑,尝试把 Person.hashcode函数注释再运行程序,你会明白 Map<Person,String> personStringMap = new HashMap<Person, String>(10); personStringMap.put(Tom,"Tom"); personStringMap.put(Deal,"Deal"); System.out.println("Tom's nickname is : "+personStringMap.get(Tom)); System.out.println("Jack's nickname is : "+personStringMap.get(Jack)); System.out.println("Deal's nickname is : "+personStringMap.get(Deal)); System.out.println("is Tom equals to Jack : "+Tom.equals(Jack)); // list 的contains 是根据equal方法来判断的 List<Person> persons = new ArrayList<Person>(); persons.add(Tom); persons.add(Deal); System.out.println("persons contains Jack: " + persons.contains(Jack)); System.out.println("persons contains Deal: " + persons.contains(Deal)); } }
参考 http://www.cnblogs.com/kismetv/p/7191736.html
参考 https://www.cnblogs.com/dolphin0520/p/3681042.html