- 浏览: 146333 次
- 性别:
- 来自: 上海
-
文章分类
最新评论
-
zi_wu_xian:
docx格式的word文件虽然是zip格式的,也可以看到xml ...
用Java操作Office 2007 -
MyDreamNotDream:
看代码看到这里很不容易呢。
Java中HashMap的实现原理 -
四书五经:
to 楼上的 SonofGod :这个时候这样去获取:如果(值 ...
Java中HashMap的实现原理 -
SonofGod:
请问 楼主 在疑问3中。多个key的hash值一样的话,存储时 ...
Java中HashMap的实现原理 -
SonofGod:
请问 楼主 在疑问2中。多个可以的hash得到一样的hash值 ...
Java中HashMap的实现原理
Java进阶:深入equals方法对象相等比较
发布时间:2007.06.19 05:55 来源:赛迪网技术社区 作者:dxaw
equals方法的重要性毋须多言,只要你想比较的两个对象不愿是同一对象,你就应该实现equals方法,让对象用你认为相等的条件来进行比较.下面的内容只是API的规范,没有什么太高深的意义,但我之所以最先把它列在这儿,是因为这些规范在事实中并不是真正能保证得到实现.
1.对于任何引用类型, o.equals(o)==true 成立.
2.如果 o.equals(o1)==true 成立,那么o1.equals(o)==true也一定要成立.
3.如果 o.equals(o1)==true 成立且o.equals(o2)==true 成立,那么o1.equals(o2)==true 也成立.
4.如果第一次调用o.equals(o1)==true 成立再o和o1没有改变的情况下以后的任何次调用都成立.
5.o.equals(null)==true 任何时间都不成立.
以上几条规则并不是最完整的表述,详细的请参见API文档.对于Object类,它提供了一个最最严密的实现,那就是只有是同一对象是,equals方法才返回true,也就是人们常说的引用比较而不是值比较.这个实现严密得已经没有什么实际的意义,所以在具体子类(相对于Object来说)中,如果我们要进行对象的值比较,就必须实现自己的equals方法.
先来看一下以下这段程序:
public boolean equals(Object obj)
{
if (obj == null) return false;
if (!(obj instanceof FieldPosition))
return false;
FieldPosition other = (FieldPosition) obj;
if (attribute == null) {
if (other.attribute != null) {
return false;
}
}
else if (!attribute.equals(other.attribute)) {
return false;
}
return (beginIndex == other.beginIndex
&& endIndex == other.endIndex
&& field == other.field);
}
这是JDK中java.text.FieldPosition的标准实现,似乎没有什么可说的.我信相大多数或绝大多数程序员认为,这是正确的合法的equals实现.毕竟它是JDK的API实现啊.还是让我们以事实来说话吧:
package debug;
import java.text.*;
public class Test {
public static void main(String[] args) {
FieldPosition fp = new FieldPosition(10);
FieldPosition fp1 = new MyTest(10);
System.out.println(fp.equals(fp1));
System.out.println(fp1.equals(fp));
}
}
class MyTest extends FieldPosition{
int x = 10;
public MyTest(int x){
super(x);
this.x = x;
}
public boolean equals(Object o){
if(o==null) return false;
if(!(o instanceof MyTest )) return false;
return ((MyTest)o).x == this.x;
}
}
运行一下看看会打印出什么:
System.out.println(fp.equals(fp1));打印true
System.out.println(fp1.equals(fp));打印flase
两个对象,出现了不对称的equals算法.问题出在哪里(脑筋急转弯:当然出在JDK实现的BUG)?我相信有太多的程序员(除了那些根本不知道实现equals方法的程序员外)在实现equals方法时都用过instanceof运行符来进行短路优化的,实事求是地说很长一段时间我也这么用过。太多的教程,文档都给了我们这样的误导。而有些稍有了解的程序员可能知道这样的优化可能有些不对但找不出问题的关键。另外一种极端是知道这个技术缺陷的骨灰级专家就提议不要这样应用。
我们知道,"通常"要对两个对象进行比较,那么它们"应该"是同一类型。所以首先利用instanceof运行符进行短路优化,如果被比较的对象不和当前对象是同一类型则不用比较返回false,但事实上,"子类是父类的一个实例",所以如果 子类 o instanceof 父类,始终返回true,这时肯定不会发生短路优化,下面的比较有可能出现多种情况,一种是不能造型成子类而抛出异常,另一种是父类的private 成员没有被子类继承而不能进行比较,还有就是形成上面这种不对称比较。可能会出现太多的情况。那么,是不是就不能用 instanceof运行符来进行优化?答案是否定的,JDK中仍然有很多实现是正确的,如果一个class是final的,明知它不可能有子类,为什么不用 instanceof来优化呢?为了维护SUN的开发小组的声誉,我不说明哪个类中,但有一个小组成员在用这个方法优化时在后加上了这样的注释:
if (this == obj) // quick check
return true;
if (!(obj instanceof XXXXClass)) // (1) same object?
return false;
可能是有些疑问,但不知道如何做(不知道为什么没有打电话给我......)那么对于非final类,如何进行类型的quick check呢?
if(obj.getClass() != XXXClass.class) return false;
用被比较对象的class对象和当前对象的class比较,看起来是没有问题,但是,如果这个类的子类没有重新实现equals方法,那么子类在比较的时候,obj.getClass() 肯定不等于XXXCalss.class,也就是子类的equals将无效,所以if(obj.getClass() != this.getClass()) return false;才是正确的比较。
2008-07-26
java对象equals方法的重写
什么时候需要重写equals()?
我们知道每一个java类都继承自Object类,equals()是Object类中提供的方法之一。那么,让我们先来看看Object#equals()在Java中的原代码:
public boolean equals(Object obj)
{
return (this == obj);
}
可以看出,只有当一个实例等于它本身的时候,equals()才会返回true值。通俗地说,此时比较的是两个引用是否指向内存中的同一个对象,也可以称做是否实例相等。而我们在使用equals()来比较两个指向值对象的引用的时候,往往希望知道它们逻辑上是否相等,而不是它们是否指向同一个对象。在这样的情况下, 如果超类也没有重写equals()以实现期望的行为,这时我们就需要重写equals方法。而且这样做也使得这个类的实例可以被用做映射表(map)的键,或者集合(set)的元素,并使映射表或者集合表现出预期的行为。
Object类提供的equals方法只是一个很简单的,不能适应应用程序有特殊要求的情况。
比如网络对象,带有volatile属性的对象,或是带有多层子对象的复合对象,等等,是不能像String一类的对象进行简单比较的,所以提供了这样一个机制,就像serializable接口一样,既有默认的序列化方法,也提供了程序自己定制,覆盖默认方式的可能性。
Object仅仅提供了一个对引用的比较,如果两个引用不是同一个那就返回false,这是无法满足大多数对象比较的需要的,所以要覆盖。
怎样重写equals()方法?
重写equals()方法看起来非常简单,但是有许多改写的方式会导致错误,并且后果非常严重。要想正确改写equals()方法,你必须要遵守它的通用约定。下面是约定的内容,来自java.lang.Object的规范:
equals方法实现了等价关系(equivalence relation):
1. 自反性:对于任意的引用值x,x.equals(x)一定为true。
2. 对称性:对于任意的引用值x 和 y,当x.equals(y)返回true时,
y.equals(x)也一定返回true。
3. 传递性:对于任意的引用值x、y和z,如果x.equals(y)返回true,
并且y.equals(z)也返回true,那么x.equals(z)也一定返回true。
4. 一致性:对于任意的引用值x 和 y,如果用于equals比较的对象信息没有被修
改,多次调用x.equals(y)要么一致地返回true,要么一致地返回false。
5. 非空性:对于任意的非空引用值x,x.equals(null)一定返回false。
接下来我们通过实例来理解上面的约定。我们首先以一个简单的非可变的二维点类作为开始:
public class Point{
private final int x;
private final 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;
}
}
假设你想要扩展这个类,为一个点增加颜色信息:
public class ColorPoint extends Point{
private Color color;
public ColorPoint(int x, int y, Color color){
super(x, y);
this.color = color;
}
//override equasl()
public boolean equals(Object o){
if(!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint)o;
return super.equals(o) && cp.color==color;
}
}
我们重写了equals方法,只有当实参是另一个有色点,并且具有同样的位置和颜色的时候,它才返回true。可这个方法的问题在于,你在比较一个普通点和一个有色点,以及反过来的情形的时候,可能会得到不同的结果:
public static void main(String[] args){
Point p = new Point(1, 2);
ColorPoint cp = new ColorPoint(1, 2, Color.RED);
System.out.println(p.equals(cp));
System.out.println(cp.eqauls(p));
}
运行结果:
true
false
这样的结果显然违反了对称性,你可以做这样的尝试来修正这个问题:让ColorPoint.equals在进行“混合比较”的时候忽略颜色信息:
public boolean equals(Object o){
if(!(o instanceof Point))
return false;
//如果o是一个普通点,就忽略颜色信息
if(!(o instanceof ColorPoint))
return o.equals(this);
//如果o是一个有色点,就做完整的比较
ColorPoint cp = (ColorPoint)o;
return super.equals(o) && cp.color==color;
}
这种方法的结果会怎样呢?让我们先来测试一下:
public static void main(String[] args){
ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
System.out.println(p1.equals(p2));
System.out.println(p2.equals(p1));
System.out.println(p2.equals(p3));
System.out.println(p1.eqauls(p3));
}
运行结果:
true
true
true
false
这种方法确实提供了对称性,但是却牺牲了传递性(按照约定,p1.equals(p2)和p2.eqauals(p3)都返回true,p1.equals(p3)也应返回true)。要怎么解决呢?事实上,这是面向对象语言中关于等价关系的一个基本问题。要想在扩展一个可实例化的类的同时,既要增加新的特征,同时还要保留equals约定,没有一个简单的办法可以做到这一点。新的解决办法就是不再让ColorPoint扩展Point,而是在ColorPoint中加入一个私有的Point域,以及一个公有的视图(view)方法:
public class ColorPoint{
private Point point;
private Color color;
public ColorPoint(int x, int y, Color color){
point = new Point(x, y);
this.color = color;
}
//返回一个与该有色点在同一位置上的普通Point对象
public Point asPoint(){
return point;
}
public boolean equals(Object o){
if(o == this)
return true;
if(!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint)o;
return cp.point.equals(point)&&
cp.color.equals(color);
}
}
还有另外一个解决的办法就是把Point设计成一个抽象的类(abstract class),这样你就可以在该抽象类的子类中增加新的特征,而不会违反equals约定。因为抽象类无法创建类的实例,那么前面所述的种种问题都不会发生。
重写equals方法的要点:
1. 使用==操作符检查“实参是否为指向对象的一个引用”。
2. 使用instanceof操作符检查“实参是否为正确的类型”。
3. 把实参转换到正确的类型。
4. 对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹
配。对于既不是float也不是double类型的基本类型的域,可以使用==操作符
进行比较;对于对象引用类型的域,可以递归地调用所引用的对象的equals方法;
对于float类型的域,先使用Float.floatToIntBits转换成int类型的值,
然后使用==操作符比较int类型的值;对于double类型的域,先使用
Double.doubleToLongBits转换成long类型的值,然后使用==操作符比较
long类型的值。
5. 当你编写完成了equals方法之后,应该问自己三个问题:它是否是对称的、传
递的、一致的?(其他两个特性通常会自行满足)如果答案是否定的,那么请找到
这些特性未能满足的原因,再修改equals方法的代码。
发表评论
-
微信收货地址共享接口-终极解决
2015-06-25 13:10 8385最近要接入微信的收货地址共享接口,总是不成功,折腾了好 ... -
Java中HashMap的实现原理
2011-04-28 14:30 2819昨天有人来公司面试,因为面试的地方和我坐的地方比较近,所以也听 ... -
java注解(annotation)简介
2010-06-13 10:10 1368[Java 5.0] Annotation – @Deprec ... -
quartz和spring-quartz
2010-06-13 10:03 975quartz和spring-quartz -
Java 线程实例讲解综述
2010-06-13 09:57 1051Java 线程实例讲解综述 编写具有多线程能力的程序经常会用 ... -
Java Double 精度问题总结
2010-06-13 09:56 5329使用Java,double 进行运算时,经常出现精度丢失的问题 ... -
eXtremeComponents的eXtremeTable分页特性
2010-05-14 17:27 3397下面是我使用的例子: <ec:table ite ... -
java---final 关键字 和 static 用法
2010-03-17 13:58 905final 关键字 和 static 用法 一、final ... -
java版的escape和unescape方法
2010-03-17 09:21 2598其中unescape方法可以用来解开javascript的es ... -
StatSVN的使用说明
2010-03-04 10:27 1052一、 checkout 希望统计的版本或者分支到某个目录(不管 ... -
Velocity语法
2010-03-01 18:01 9261. 变量 (1)变量的 ... -
用KeyTool生成安全证书
2010-02-22 17:14 1161详细请见:Tomcat的帮助文档,:https://local ... -
Spring 注解学习手札
2010-02-10 10:02 889http://snowolf.iteye.com/blog/5 ... -
JDK、JRE、JVM的关系
2010-01-25 11:23 917JDK就是Java Development Kit.简单的说J ... -
类装载器学习
2010-01-22 12:54 866Java的类装载器(Class Loader)和命名空间(Na ... -
Tomcat发布项目方法
2010-01-22 10:46 2640第一种方法:在tomcat中的conf目录中,在server. ... -
理解Java ClassLoader机制
2010-01-21 16:20 934当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初 ... -
cookie和session的工作机制
2010-01-19 15:19 870转载自:http://hi.baidu.com/jmtbai/ ... -
如何设置Tomcat的JVM虚拟机内存大小
2010-01-18 14:25 993Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上 ... -
浅谈设置JVM内存分配的几个妙招
2010-01-18 14:24 1622安装Java开发软件时,默 ...
相关推荐
而equals()方法则是Object类的一个成员方法,用于比较对象的内容是否相等。默认情况下,它只是简单地检查引用是否相同,即与“==”行为一致。但是,对于大多数非原始类型的类(如String、Integer等),equals()通常...
在这个例子中,`equals`方法根据`name`和`age`属性来判断两个`Person`对象是否相等,而`hashCode`方法也综合考虑了这两个属性,以确保`equals`方法返回`true`的对象具有相同的哈希码。 #### 七、总结 在Java中,...
在Java编程中,正确实现`equals`方法至关重要,它不仅影响对象的比较逻辑,还直接关系到集合类(如`HashSet`、`HashMap`等)的行为。本文将深入探讨Java中`equals`方法的一些常见陷阱,并提供相应的解决方案。 ####...
同时,`equals`方法和`hashCode`方法应当一起重写,以确保对象相等时其哈希码也相等,这是HashMap等数据结构正确工作的前提。 总的来说,深入理解并正确实现`equals`方法是Java编程中的基础但关键的技能。这涉及到...
在Java编程语言中,`equals()`方法和`hashCode()`方法是两个非常重要的概念,它们主要用于对象的比较和哈希表的高效运作。本解析将深入探讨这两个方法的用途、实现原理以及它们之间的关联。 首先,`equals()`方法是...
- 相反,如果两个对象的`hashCode()`值相同,并不能保证这两个对象通过`equals()`方法比较也是相等的。这是因为可能存在不同的对象具有相同的哈希值,这种情况称为哈希冲突。 - 为了避免哈希冲突带来的性能问题,在...
为了更清晰地阐述Java对象相等比较的深入知识,我们可以用一个例子说明。假设有一个Point类,它有两个成员变量x和y,用来表示一个点在二维空间的位置。如果我们想比较两个Point对象是否相等,我们会在Point类中重写...
- 如果两个对象相等(根据`equals()`),它们的哈希码必须相等。 - 如果两个对象的哈希码相等,它们不一定相等,但相等的对象必须具有相等的哈希码。 实现`equals()`和`hashCode()`的一个常见模式是使用Apache ...
// 输出 true,Integer类的equals方法比较的是值 Integer k = new Integer(5); Integer l = new Integer(5); System.out.println(k.equals(l)); // 输出 true,尽管k和l指向不同的对象,但值相等 ``` #### 总结 ...
5. **相等性约定**:为了保持一致性,通常还需要重写`GetHashCode`方法,以便当两个对象相等时,它们的哈希码也相等。此外,`Equals`和`GetHashCode`应满足以下契约: - 如果`a.Equals(b)`为`true`,那么`a....
在重写 `hashCode()` 时,通常会基于 `equals()` 方法中用于比较对象相等性的属性来计算哈希码。例如,如果一个类有三个属性(a, b, c),则可以这样重写: ```java @Override public int hashCode() { int result...
相反,"equals()"是Object类的一个方法,被设计用于比较对象的内容。默认情况下,"equals()"方法与"=="行为一致,即比较对象引用。但大多数类(包括String类)都重写了这个方法,使其能够比较对象的值。对于字符串,...
然而,对于非原始类型,如果我们要比较的是对象的内容而不是引用,就需要重写`equals()`方法。`equals()`方法默认继承自`Object`类,其行为与`==`相同,但在自定义类中,我们通常需要根据业务逻辑覆盖它以实现具体的...
在Java编程语言中,`equals()`方法是用来比较两个对象的内容是否相等的,而`==`操作符则是用来比较两个变量是否引用同一个对象。这里我们将深入探讨`equals()`方法的用法,以及它与`==`的区别,同时解决题目中提出的...
1. **一致性**:如果两个对象相等(通过`equals`方法判断),则它们的`hashCode`值必须相同。 2. **可变性**:如果两个对象的`hashCode`值相同,并不一定意味着这两个对象相等。 ### 3. 实例分析 假设我们有一个名...
然而,为了比较对象的内容,许多类(如String、Integer)都重写了“equals()”方法,使其能够比较对象的属性值。 例如,在String类中,“equals()”用于比较两个字符串的字符序列是否相同。如果两个字符串包含相同...
首先,`equals()`方法主要用于比较两个对象是否相等。在基本类型(如int, double等)中,我们使用“==”操作符来判断值是否相同。然而,对于对象来说,"相等"并不总是意味着引用相同。默认情况下,`equals()`方法会...
使用 equals() 方法比较 Integer 对象 为了正确地比较两个 Integer 对象,我们应该使用 equals() 方法,而不是使用 "==" 运算符。equals() 方法将比较两个对象的实际值,而不是它们的引用。 例如,在下面的代码中...
深入解析Java对象的equals()和hashCode()的使用 在Java语言中,equals()和hashCode()两个函数的使用是紧密配合的,你要是自己设计其中一个,就要设计另外一个。在多数情况下,这两个函数是不用考虑的,直接使用它们...
在Java编程语言中,`equals`方法和`==`操作符都是用来比较对象之间是否相等的重要工具,但它们之间存在显著的区别。理解这些差异对于编写高效且无误的代码至关重要。 #### 二、基本概念 1. **==(双等号)**: - ...