# 重写equal()时为什么也得重写hashCode()之深度解读equal方法与hashCode方法

http://blog.csdn.net/javazejian/article/details/51348320

1.equals()的所属以及内部原理（即Object中equals方法的实现原理）

public boolean equals(Object obj) { return (this == obj); }

2.equals()与‘==’的区别

package com.zejian.test;
public class Car {
private int batch;
public Car(int batch) {
this.batch = batch;
}
public static void main(String[] args) {
Car c1 = new Car(1);
Car c2 = new Car(1);
System.out.println(c1.equals(c2));
System.out.println(c1 == c2);
}
}

false

false

@Override
public boolean equals(Object obj) {
if (obj instanceof Car) {
Car c = (Car) obj;
return batch == c.batch;
}
return false;
}

true

false

3.equals()的重写规则

package com.zejian.test;
public class Car {
private int batch;
public Car(int batch) {
this.batch = batch;
}
public static void main(String[] args) {
Car c1 = new Car(1);
Car c2 = new Car(1);
Car c3 = new Car(1);
System.out.println("自反性->c1.equals(c1)：" + c1.equals(c1));
System.out.println("对称性：");
System.out.println(c1.equals(c2));
System.out.println(c2.equals(c1));
System.out.println("传递性：");
System.out.println(c1.equals(c2));
System.out.println(c2.equals(c3));
System.out.println(c1.equals(c3));
System.out.println("一致性：");
for (int i = 0; i < 50; i++) {
if (c1.equals(c2) != c1.equals(c2)) {break;
}
}
System.out.println("equals方法遵守一致性！");
System.out.println("与null比较：");
System.out.println(c1.equals(null));
}
@Override
br/>System.out.println("equals方法没有遵守一致性！");
break;
}
}
System.out.println("equals方法遵守一致性！");
System.out.println("与null比较：");
System.out.println(c1.equals(null));
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Car) {
Car c = (Car) obj;
return batch == c.batch;
}
return false;
}
}

true

true

true

true

true

equals方法遵守一致性！

false

package com.zejian.test;
public class BigCar extends Car {
int count;
public BigCar(int batch, int count) {
super(batch);
this.count = count;br/>}
@Override
public boolean equals(Object obj) {
if (obj instanceof BigCar) {
BigCar bc = (BigCar) obj;
return super.equals(bc) && count == bc.count;
}
return false;
}
public static void main(String[] args) {
Car c = new Car(1);
BigCar bc = new BigCar(1, 20);
System.out.println(c.equals(bc));
System.out.println(bc.equals(c));
}
}

true

false

true

true

false

bc，bc2，c的批次都是相同的，按我们之前的需求应该是相等，而且也应该符合equals的传递性才对。但是事实上运行结果却不是这样，违背了传递性。出现这种情况根本原因在于：

4.equals()的重写规则之必要性深入解读

list.contains(a)->true

list.contains(b)->false

list.contains(a)->true

list.contains(b)->true

19行和24行的输出没什么好说的，将a，b分别加入list中，list中自然会含有a，b。但是为什么20行和23行结果会不一样呢？我们先来看看contains方法内部实现

b.equals(a[i]),a[i]是集合中的元素集合中的类型而且为A类型(只添加了a对象)，虽然B继承了A,但此时

5.为什么重写equals()的同时还得重写hashCode()

package com.zejian.test;
public class HashCodeTest {
public static void main(String[] args) {
int hash=0;
String s="ok";
StringBuilder sb =new StringBuilder(s);

```System.out.println(s.hashCode()+"  "+sb.hashCode());

String t = new String("ok");
StringBuilder tb =new StringBuilder(s);
System.out.println(t.hashCode()+"  "+tb.hashCode());
}```

}

3548 1829164700

3548 2018699554

package com.zejian.test;
public class Model {
private String name;
private double salary;
private int sex;

```@Override
public int hashCode() {
return name.hashCode()+new Double(salary).hashCode()
+ new Integer(sex).hashCode();
}```
• new Integer(sex).hashCode();
}
}
java 7还提供了另外一个方法java.util.Objects.hash(Object... objects),当我们需要组合多个散列值时可以调用该方法。进一步简化上述的代码：
package com.zejian.test;
import java.util.Objects;
public class Model {
private String name;
private double salary;
private int sex;
// @Override
// public int hashCode() {
// return Objects.hashCode(name)+new Double(salary).hashCode()
// + new Integer(sex).hashCode();
// }

@Override
public int hashCode() {
return Objects.hash(name,salary,sex);
}
}
好了，到此hashCode()该介绍的我们都说了，还有一点要说的如果我们提供的是一个数值类型的变量的话，那么我们可以调用Arrays.hashCode()来计算它的散列码，这个散列码是由数组元素的散列码组成的。接下来我们回归到我们之前的问题，重写equals方法时也必须重写hashCode方法。在Java API文档中关于hashCode方法有以下几点规定（原文来自java深入解析一书）。

package com.zejian.test;
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
Map<String,Value> map1 = new HashMap<String,Value>();
String s1 = new String("key");
String s2 = new String("key");
Value value = new Value(2);
map1.put(s1, value);
System.out.println("s1.equals(s2):"+s1.equals(s2));
System.out.println("map1.get(s1):"+map1.get(s1));
System.out.println("map1.get(s2):"+map1.get(s2));

```Map<Key,Value> map2 = new HashMap<Key,Value>();
Key k1 = new Key("A");
Key k2 = new Key("A");
map2.put(k1, value);
System.out.println("k1.equals(k2):"+s1.equals(s2));
System.out.println("map2.get(k1):"+map2.get(k1));
System.out.println("map2.get(k2):"+map2.get(k2));
}

/**
* 键
* @author zejian
*
*/
static class Key{
private String k;
public Key(String key){
this.k=key;
}

@Override
public boolean equals(Object obj) {
if(obj instanceof Key){
Key key=(Key)obj;
return k.equals(key.k);
}
return false;
}
}

/**
* 值
* @author zejian
*
*/
static class Value{
private int v;

public Value(int v){
this.v=v;
}

@Override
public String toString() {
return "类Value的值－－>"+v;
}
}```

public class Person {
protected String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name){
this.name = name;
}
public boolean equals(Object object){
if(object instanceof Person){
Person p = (Person) object;
if(p.getName() == null || name == null){
return false;
}
else{
return name.equalsIgnoreCase(p.getName ());
}
}
return false;
}
}

public class Employee extends Person{
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Employee(String name,int id){
super(name);
this.id = id;
}
/**

• 重写equals()方法
*/
public boolean equals(Object object){
if(object instanceof Employee){
Employee e = (Employee) object;
return super.equals(object) && e.getId() == id;
}
return false;
}
}
上面父类 Person 和子类 Employee 都重写了 equals(),不过 Employee 比父类多了一个id属性,而且这里我们并没有统一语义。测试代码如下：
public class Test {
public static void main(String[] args) {
Employee e1 = new Employee("chenssy", 23);
Employee e2 = new Employee("chenssy", 24);
Person p1 = new Person("chenssy");
System.out.println(p1.equals(e1));
System.out.println(p1.equals(e2));
System.out.println(e1.equals(e2));
}
}
上面代码我们定义了两个员工和一个普通人，虽然他们同名，但是他们肯定不是同一人，所以按理来说结果应该全部是 false，但是事与愿违，结果是：true、true、false。对于那 e1!=e2 我们非常容易理解，因为他们不仅需要比较 name,还需要比较 ID。但是 p1 即等于 e1 也等于 e2，这是非常奇怪的，因为 e1、e2 明明是两个不同的类，但为什么会出现这个情况？首先 p1.equals(e1)，是调用 p1 的 equals 方法，该方法使用 instanceof 关键字来检查 e1 是否为 Person 类，这里我们再看看 instanceof：判断其左边对象是否为其右边类的实例，也可以用来判断继承中的子类的实例是否为父类的实现。他们两者存在继承关系，肯定会返回 true 了，而两者 name 又相同，所以结果肯定是 true。所以出现上面的情况就是使用了关键字 instanceof，这是非常容易导致我们“钻牛角尖”。故在覆写 equals 时推荐使用 getClass 进行类型判断。而不是使用 instanceof（除非子类拥有统一的语义）。
7.编写一个完美equals()的几点建议

1）显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量（参数名命名，强制转换请参考建议5）

2）检测this与otherObject是否引用同一个对象 ：if(this == otherObject) return true;（存储地址相同，肯定是同个对象，直接返回true）

3) 检测otherObject是否为null ，如果为null,返回false.if(otherObject == null) return false;

4) 比较this与otherObject是否属于同一个类 （视需求而选择）

5) 将otherObject转换为相应的类类型变量：ClassName other = (ClassName) otherObject;

6) 现在开始对所有需要比较的域进行比较 。使用==比较基本类型域，使用equals比较对象域。如果所有的域都匹配，就返回true，否则就返回flase。

Java核心技术 第一卷：基础知识

Java深入分析