前段时间一直在工做中使用Java,因为有一些C++功底,因而简单看了一下Java相关的语法便开始编写代码,结果在建立一个自定义类,并将自定义类放入ArrayList中,以后查找ArrayList是否有此元素的时候,发现怎么也查询不到对应的元素。在网上搜了一下资料,发现缘由是没有重写对象的equals()方法,致使没法查找到对应的对象。以后由查了与之联系的相关资料,便有了如下的总结。html
这篇总结的形式是提出个问题,而后给出问题的答案。这是目前学习知识的一种尝试,可让学习更有目的。java
答:通常在咱们须要进行值比较的时候,是须要重写对象的equals方法的。而例外状况在《effective java》的第7条“在改写equals的时候请遵照通用约定”中清楚描述了。shell
咱们知道,在Java中,每一个对象都继承于Object.若是不重写,则默认的equals代码以下所示:api
public boolean euqals(Object obj){ return this == obj; }
由上面的代码能够看出,equal默认是使用“==”来判断两个对象是否相等。两个对象使用“==”比较的是对象的地址,只有两个引用指向的对象相同的时候,“==”才返回true。因此,在开头的例子中,就须要重写equals方法,让两个对象有equals的时候。oracle
答:首先,当改写equals方法时,须要保证知足它的通用约定。这些约定以下所示:ide
其实我觉的一个简单的方法是参照String的equals方法便可,官方出版,知足各类要求。其代码以下所示函数
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = count; if (n == anotherString.count) { char v1[] = value; char v2[] = anotherString.value; int i = offset; int j = anotherString.offset; while (n– != 0) { if (v1[i++] != v2[j++]) return false; } return true; } } return false; }
函数的解释以下所示:学习
更详细的信息,仍是请看《effective java》的第7条“在改写equals的时候请遵照通用约定”。this
答:大体须要注意如下几点:code
若修改equals方法,也请修改hashCode方法
首先这个是语言的一个约定,这么作的一个缘由是当此对象做为哈希容器的元素时,须要依赖hashCode,对象默认的hashCode是返回一个此对象特有的hashCode,不一样的对象的hashCode返回值是不同的,而哈希容器处理元素时,是按照对象的哈希值将对象分配到不一样的桶中,若咱们不重写对象的hashCode,那么值相等的对象产生的哈希值也会不一样,这样当在哈希容器中查找时,会找不到对应的元素。
更详细的信息请看《effective Java》的第8条“改写equals时老是要改写hashCode”。
重写时保证函数声明的正确
请注意equals的声明是
public boolean equals(Object obj)
参数类型是Object,若是参数类型是此对象类型的话,以下:
class Point{ final int x; final int y; public void Point(int x, int y) this.x = x; this.y = y; } public boolean euqals(Point obj){ return (this.x == obj.x && this.y == obj.y); } }
下面代码执行是按照咱们的预期执行的。
Point a(1, 2); Poinr b(1, 2); System.out.println(a.equals(b));// 输出true
可是若是将类A放入容器中,则会出问题
import java.util.HashSet; HashSet<Point> coll = new HashSet<Point>(); coll.add(a); System.out.println(coll.contains(b));// 输出false
这是因为HashSet中的contains方法中调用的是equals(Object obj),而Point中的equals(Object obj)还是Object的equals,这个方法在前面已经说过了,比较的是对象的地址,因此在coll中调用contains(b)时,固然得不到true。
当有继承关系时注意equals的正确
当一个类重写equals方法后,另外一个类继承此类,此时,可能会违反前面说到的对称性,代码以下所示:
public class ColoredPoint extends Point { private final Color color; public ColoredPoint(int x, int y, Color color) { super(x, y); this.color = color; } @Override public boolean equals(Object other) { boolean result = false; if (other instanceof ColoredPoint) { ColoredPoint that = (ColoredPoint) other; result = (this.color.equals(that.color) && super.equals(that)); } return result; } }
当咱们做比较时
Point p = new Point(1, 2); ColoredPoint cp = new ColoredPoint(1, 2, Color.RED); System.out.println(p.equals(cp)); //输出ture System.out.println(cp.equals(p)); //输出false
缘由是当调用Point.equals的时候,只比较了Point的x和y坐标,同时ColoredPoint也是Point类型,因此上面第三行代码相等,而调用ColoredPoint的时候,Point不是ColoredPoint类型,这样就致使第四行代码输出false。
若咱们忽略Color的信息来比较呢,例如将ColoredPoint的equals方法改成:
@overwrite public boolean equals(Object obj){ if((obj instanceof Point)){ return false; } if(!(obj instanceof ColoredPoint)){ return obj.equals(this); } return super.equals(obj) && ((ColoredPoint)obj).color == color; }
这样就保证了对称性,可是却违反了传递性,即下面的状况:
ColoredPoint cp1 = new ColoredPoint(1, 2, Color.RED); Point p = new Point(1, 2); ColoredPoint cp2 = new ColoredPoint(1, 2, Color.BLUE); System.out.println(cp1.equals(p)); //true System.out.println(p.equals(cp2)); //true System.out.println(cp1.equals(cp2)); //false
面对这种状况,大体有两种解决方案,一种酷壳的文章--如何在Java中避免equals方法的隐藏陷阱的最后一条,断绝了Point和ColoredPoint相等的可能,这是一种处理方法,认为Point和ColoredPoint是不一样的。另外一种方法是effective Java上提出的,使用聚合而不是继承,将Point做为ColoredPoint的一个成员变量。目前我倾向于这种方法,由于聚合比继承更灵活,耦合更低。这种方法的代码以下所示:
class ColoredPoint{ private final Point point; private final Color color; public Point asPoint(){ return point; } public boolean equals(Object obj){ boolean ret = false; if(obj instanceof ColoredPoint){ ColoredPoint that = (ColoredPoint)obj; ret = that.point.equals(point) && color.equals(that.color); } return ret; } }
当ColoredPoint须要比较坐标时,能够调用asPoint方法来转化为坐标进行比较。其余状况比较坐标和颜色,这样就能够解决上面关于对称性和传递性的问题了。
以上就是全文的内容,因为水平有限,文章中不免会有错误,但愿你们指正。谢谢
[1/30]
参考资料: