如何重写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; }
相关文章推荐
- 为什么要重写java中对象的equals方法和hashCode方法以及如何重写
- 如何重写hashCode()和equals()方法
- 如何正确的重写equals() 和 hashCode()方法
- 关于为什么要重写hashCode()方法和equals()方法及如何重写
- 如何重写Object的hashCode和equals方法
- Set是如何实现元素不重复的&重写equals()方法之后也必须重写hashCode()方法
- 如何正确的重写equals() 和 hashCode()方法
- 为什么要重写hashCode()方法和equals()方法以及如何进行重写
- 为什么要重写hashCode()方法和equals()方法及如何重写
- 如何重写equals和hashCode方法
- 如何重写hashCode()和equals()方法
- 关于为什么要重写hashCode()方法和equals()方法及如何重写
- 为什么重写equals 和hashCode方法,如何重写
- 如何正确的重写equals() 和 hashCode()方法
- 关于为什么要重写hashCode()方法和equals()方法及如何重写
- 关于为什么要重写hashCode()方法和equals()方法及如何重写
- 如何重写hashCode()和equals()方法
- 如何正确的重写equals() 和 hashCode()方法
- 正确重写hashcode hashcode与equals方法 集合元素如何判断是否相等 集合如何查看是否包含某个元素
- 如何重写hashCode()和equals()方法