关于==,equals和hashCode的比较是一个经常被问到的问题,要牢记答案必须先知道其原理,下面先从他们的原理,使用和注意事项来阐述他们:
1.原理
==比较的是2个对象是否指向的同一个引用,只有指向同一个引用才返回true;
equals方法在Object类中return this == obj,默认指向同一个引用才返回true;
hashCode方法在Object类中定义返回一个int类型。
2.使用
equals:
通常一个类需要定义equals方法是为了比较该类中不同实例中全部或者部分属性值是否相等,我们只需要在自定义的equals方法中去比较我们所关心的字段就好。如果这些字段全部相等,就可以返回true,否则返回false;至于Object类中使用==来判断是出于性能考虑,毕竟比较字段更浪费性能,既然他们指向的引用是同一个,那么他们包含的字段自然也相等。下面我们看一下String类中是如何重写equals方法的。
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
1.如果指向相同引用,返回true,同Object类一样;
2.如果是不同引用,对象类型又不一致,返回false;这里要说明的是,通常使用equals方法都是在同一个类型,不同实例之间使用。如果存在继承关系的话,可以使用getClass()方法来比较他们是否属于同一个类模板。因为 子类 instanceof 父类 会返回true;
3.比较各属性值。String类型底层是一个char数组,比较每一个char的值是否相等,全部相等才返回true。
hashCode:
在我们定义equals方法后都会去自定义一个hashCode方法,提供计算散列值。之所以这样做是为了更好的支持一些基于hash算法的容器,Hashtable, HashMap, HashSet 等。
通常重写hashCode方法会将equals方法中使用到的字段全部纳入计算当中,得出一个最终的int值。
下面我们看一下String类中是如何重写hashCode方法的:
/** Cache the hash code for the string */ private int hash; // Default to 0
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; }
定义了一个算法 h = 31 * h + val[i] 来计算hashCode;其中每一个字符都参与该计算。之所以使用31,并不是硬性要求,而是为了避免hash碰撞产生重复值,故而使用一个质数。
3.注意事项
网上很多文章都在提equals方法和hashCode方法需要遵守一些规则,实际上这些规则是定义在Object类的方法注释中的,大家可以去读一读源码注释。很多笔试题中都会出现给定一个equals方法,让笔试者选择或者写出一个hashCode方法的实现,实际上就是考察的这些规则。下面我先列举出需要注意的规范:
关于equals方法
- 自反性。对于任何非null的引用值x,x.equals(x)必须返回true。
- 对称性。对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true
- 传递性。对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。
- 一致性。对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用该x.equals(y)就会一直地返回true,或者一致地返回false。
- 对于任何非null的引用值x,x.equals(null)必须返回false。
关于hashC方法
在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)