简单的一句话结论就是:保证你定义的“equal”的对象拥有相同的hash code。
1)两个对象equals()为真,则它们的hashCode() 一定相同
2)两个对象hashCode()相同,equals()不一定为真这两个Object方法如果没有很好的定义,可能会产生使用者不希望看到的效果。
2)实现hashCode(),原则如本答案第一句话所示,并尽量使用对象自有特性作为生成因子以减小冲突的概率。
equals方法实现等价关系
1、自反性。
对应任意引用值x,x.equals(x),一定为真。
即一个对象必须等于其自身。
2、对称性。
对于任意引用值x和y,x.equals(y)返回真时,y.equals(x)也一定为真
3、传递性。
对于任意引用值x、y和z,x.equals(y)返回真,且y.equals(z)返回真,则x.equals(z)一定返回真。
4、一致性。
对于任意引用值x和y,如果用于equals比较对象的信息没有被修改的话,那么多次调用x.equals(y)要么都是true,要么都是false
5、对于任意的非空引用值x,x.equals(null) 一定返回false
下面的例子违反了对称性
package equals;
import java.util.ArrayList;
import java.util.List;
public final class CaseInsensitive {
private String s;
public CaseInsensitive(String s) {
if(s == null) {
throw new UnsupportedOperationException();
}
this.s = s;
}
@Override
public boolean equals(Object o) {
if(o instanceof CaseInsensitive) {
return s.equalsIgnoreCase(((CaseInsensitive) o).s);
}
// 如果传进来的是String对象
if(o instanceof String) {
return s.equalsIgnoreCase(((String)o)) ;
}
return false;
}
public static void main(String[] args) {
CaseInsensitive o = new CaseInsensitive("CSP");
String s = "csp";
System.out.println(o.equals(s));// 返回true
System.out.println(s.equals(o));;// 返回 false
// 根据对称性,o 与 s 返回true,那么 s 与 o 也应该返回true,而这里却返回false,违反了对称性。原因是String的equals方法,不知道要忽略大小写
//如果将o加入集合中,则返回false,而我们的原意是想让他返回true
List<CaseInsensitive> list = new ArrayList<CaseInsensitive>();
list.add(o);
System.out.println(list.contains(s)); // 返回false
}
}
下面例子违反传递性
package equals;
/**
* Created by Administrator on 2017/9/23.
*/
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public boolean equals(Object o) {
if(!(o instanceof Point)) {
return false;
}
Point p = (Point) o;
return p.x == x && p.y == y;
}
}
package equals;
public class ColorPoint extends Point {
private String color;
public ColorPoint(int x, int y,String color) {
super(x, y);
this.color = color
}
public boolean equals(Object o) {
if(!(o instanceof ColorPoint)) {
return false;
}
ColorPoint p = (ColorPoint) o;
return super.equals(o) && p.color.equals(color);
}
public static void main(String[] args) {
ColorPoint cp = new ColorPoint(1,1,"red");
Point p = new Point(1,1);
System.out.println(cp.equals(p)); //false
System.out.println(p.equals(cp)); //true
// 违反了对称性。因为ColorPoint 与 Point 比较时,多了个color属性
}
}
我们可以对ColorPoint进行修改,让其满足对称性,在混合比较时,忽略颜色信息
....
public boolean equals(Object o) {
if(!(o instanceof Point)) {
return false;
}
//对象是Point类型,不是ColorPoint,进行父类级别的比较
if(!(o instanceof ColorPoint)) {
return o.equals(this);
}
ColorPoint p = (ColorPoint) o;
return super.equals(o) && p.color.equals(color);
}
....
public static void main(String[] args) {
ColorPoint cp = new ColorPoint(1,1,"red");
Point p = new Point(1,1);
ColorPoint cp1 = new ColorPoint(1,1,"black");
System.out.println(cp.equals(p)); //true
System.out.println(p.equals(cp1)); //true
System.out.println(cp.equals(cp1)); //false
// 违反了传递性
}
以上代码虽然符合对称性,但是却违反了传递性
想要解决以上问题,只能使用复合来代替继承
public class ColorPoint {
private String color;
private Point p;
public ColorPoint(String color,Point p) {
this.color = color;
this.p = p;
}
public Point getP() {
return p;
}
@Override
public boolean equals(Object o) {
if(!(o instanceof ColorPoint)) {
return false;
}
ColorPoint cp = (ColorPoint) o;
return cp.p.equals(p) && cp.color.equals(color);
}
}
当你重写了equals()时,也应该重写hashcode();
来看当一个类重写了equals()方法时,没有重写hashCode方法,并将其放入Map中
import java.util.HashMap;
import java.util.Map;
public final class CaseInsensitive {
private int x;
private int y;
private String s;
public CaseInsensitive(int x,int y,String s) {
this.s = s;
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(!(o instanceof CaseInsensitive) || o == null) {
return false;
}
CaseInsensitive cp = (CaseInsensitive) o;
return cp.s.equals(s) && cp.x == x && cp.y == y;
}
public static void main(String[] args) {
CaseInsensitive c = new CaseInsensitive(1,1,"cc");
CaseInsensitive c1 = new CaseInsensitive(1,1,"cc");
Map<CaseInsensitive,String> m = new HashMap<CaseInsensitive,String>();
m.put(c,"c");
System.out.println(m.get(new CaseInsensitive(1,1,"cc"))); // null
}
}
按照我们的意思,应该是返回 c 的,但是实际上返回的是 null
原因在于,没有重写hashCode()方法,Map在放入元素时,是先根据对象的散列值进行存放,如果散列值一样,那么则继续比较equals方法。此处默认是Object的hashCode方法。所有2个对象不一样。
让我们重写hashCode方法试下
....
@Override
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(!(o instanceof CaseInsensitive) || o == null) {
return false;
}
CaseInsensitive cp = (CaseInsensitive) o;
return cp.s.equals(s) && cp.x == x && cp.y == y;
}
//此处使用IDEA 默认生成的hashCode
@Override
public int hashCode() {
int result = x;
result = 31 * result + y;
result = 31 * result + (s != null ? s.hashCode() : 0);
return result;
}
public static void main(String[] args) {
CaseInsensitive c = new CaseInsensitive(1,1,"cc");
CaseInsensitive c1 = new CaseInsensitive(1,1,"cc");
Map<CaseInsensitive,String> m = new HashMap<CaseInsensitive,String>();
m.put(c,"c");
System.out.println(m.get(new CaseInsensitive(1,1,"cc"))); //返回c
}
此时就可以获取到c了。因为我们重写了hashCode()方法,比较2个对象的hashCode。发现hashCode一样,调用equals()方法继续比较,发现2个对象一样。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。