您的位置:首页 > 其它

如何重写hashCode()和equals()方法

2017-11-26 00:00 507 查看

一、如何重写equals()方法

1.1 注意事项

在重写equals()后,一定要重写hashCode()方法.具体原因看后文2.1节

1.2 equals()要遵守以下原则

自反性:A.equals(A)要返回true.

对称性:如果A.equals(B)返回true, 则B.equals(A)也要返回true.

传递性:如果A.equals(B)为true, B.equals(C)为true, 则A.equals(C)也要为true. 说白了就是 A = B , B = C , 那么A = C.

一致性:只要A,B对象的状态没有改变,A.equals(B)必须始终返回true.

A.equals(null) 要返回false.

1.3 《Effective Java》中记录的重写方法

模拟需求:

先定义一个程序员类(Coder):

class Coder {
private String name;
private int age;

// getters and setters
}


我们想要的是,如果2个程序员对象的name和age都是相同的,那么我们就认为这两个程序员是一个人.这时候我们就要重写其equals()方法.因为默认的equals()实际是判断两个引用是否指向内在中的同一个对象,相当于 ==

equals()方法在object类中定义如下

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

第一步: 判断是否等于自身.

if(other == this)
return true;

第二步:使用instanceof运算符判断 other 是否为Coder类型的对象.

if(!(other instanceof Coder))
return false;

第三步: 比较Coder类中你自定义的数据域(重写equals方法用到),name和age,一个都不能少

Coder o = (Coder)other;
return o.name.equals(name) && o.age == age;

放在一起如下:

@Override
public boolean equals(Object o) {
if(other == this) return true;
if(!(other instanceof Coder))
return false;
if (o instanceof Coder) {
Coder o = (Coder)other; return o.name.equals(name) && o.age == age;
}
return false;
}


java8源码中的Pari类重写的equals()方法摘录如下

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof Pair) {
Pair pair = (Pair) o;
if (key != null ? !key.equals(pair.key) : pair.key != null) return false;
if (value != null ? !value.equals(pair.value) : pair.value != null) return false;
return true;
}
return false;
}


二、如何重写hashCode()方法

2.1 重写了equals()方法后为什么要重写hashCode()?

hashcode() 方法,在object类中定义如下:

public native int hashCode();

这是一个本地方法,它的实现是根据本地机器相关的

如果重写了equals()方法后,未重写hashCode(),则按照我们的需求模拟,相同的Coder对象不一定能返回相同的hashCode值。

这样就破坏了Object规范:“如果两个对象更具equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果”

而在java集合类中,当我们在使用形如HashMap, HashSet这样前面以Hash开头的集合类时,hashCode()就会被隐式调用以来创建哈希映射关系.这样就让这些集合类失去了作用

下面我们通过一个例子来体会一下这个过程:

我们先创建2个新的Coder对象:

Coder c1 = new Coder("bruce", 10);
Coder c2 = new Coder("bruce", 10);


假定我们已经重写了Coder的equals()方法而没有重写hashCode()方法:

@Override
public boolean equals(Object other) {
System.out.println("equals method invoked!");

if(other == this) return true;
if(!(other instanceof Coder))
return false;

Coder o = (Coder)other; return o.name.equals(name) && o.age == age;
}


然后我们构造一个HashSet,将c1对象放入到set中:

Set<Coder> set = new HashSet<Coder>();
set.add(c1);
System.out.println(set.contains(c2))

我们期望contains(c2)方法返回true, 但实际上它返回了false.

在上文的模拟需求中,我们的需求是name和age都相同的Coder对象要是相同的,重写的equals()方法也符合了这个需求。

c1和c2的name和age都是相同的,为什么我把c1放到HashSet中后,再调用contains(c2)却返回false呢?这就是hashCode()在作怪了.因为你没有重写hashCode()方法,所以HashSet在查找c2时,会在不同的bucket中查找.比如c1放到05这个bucket中了,在查找c2时却在06这个bucket中找,这样当然找不到了.因此,我们重写hashCode()的目的在于,在A.equals(B)返回true的情况下,A, B 的hashCode()要返回相同的值

2.2如何重写?

《Effective Java》中给出了一个能最大程度上避免哈希冲突的写法,但我个人认为对于一般的应用来说没有必要搞的这么麻烦.如果你的应用中HashSet中需要存放上万上百万个对象时,那你应该严格遵循书中给定的方法.如果是写一个中小型的应用,那么下面的原则就已经足够使用了:

要保证Coder对象中所有的成员都能在hashCode中得到体现.

对于本例,我们可以这么写:

@Override
public int hashCode() {
int result = 17;
result = result * 31 + name.hashCode();
result = result * 31 + age;

return result;
}

其中int result = 17你也可以改成20, 50等等都可以.

String类中定义的hashcode()方法如下

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;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: