equals和hashCode是自定义类中继承自Object类的子类需要重写的两个方法。
equals方法的作用在与检测一个对象是否等于另一个对象,默认的Object类方法判断两个对象的引用是否相同。Java语言规范要求equals方法具有以下特性:
-
自反性:对于任何非空引用x,x.equals(x)返回true;
-
对称性:对于任何引用x和y,如果y.equals(x)返回true,x.equals(y)也应该返回true;
-
传递性:对于任何引用x,y,和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true。
-
一致性:如果x和y引用对象没有发生变化,x.equals(y)应该返回同样结果。
-
任意非空引用x,x.equals(null)返回false。
由于子类拥有父类所不具有的独特的域,所以继承关系中,子类equals的实现需要仔细考虑如下问题:
-
子类的相等性概念与父类的不同。如果子类的相等测试需要用到子类独有的域则对称性需求中使用getClass检测实现,例如Manager类是Employee类的子类,如果对象相等要求“分红”域相同,由于Employee中没有这个域,应该使用getClass检测相等性。
-
子类的相等性概念是由父类决定的。如果子类的相等性概念与父类一致,则使用instanceOf进行对称性测试。同样是Manager类和Employee类,如果员工ID相同则是同一个对象,那么应该使用instanceOf进行检测。
完美的equals方法建议:
-
显示参数命名为otherObject,稍后将其转换成名为other的变量;
-
检测this和otherObject是否引用同一个对象:
if(this == otherObject) return true;
-
检测是否属于同一个类:
如果子类的equals方法不同:
if(getClass() != otherObject.getClass()) return false;如果与父类的equals方法一致:
if(!(otherObject instanceOf ClassName) return false;
-
将otherObject转化为相应类型变量other;
-
检测this和other的各个域是否相同,基本类型用==,对象域用equals方法,将所有域的比较用&&连接起来。
子类重新定义equals需要调用super.equals()方法。
另外需要注意的是,所覆盖的equals方法是Object类的方法,形如:equals(Object otherObject),注意参数。
Hash code是对象导出的整形值,默认的Object类的方法是对象的存储地址。(相同内容的String的hash code相同)。
如果重新定义了equals方法就必须要重新定义hashCode方法,使得对象能够放入散列表中。equals和hashCode方法的返回结果必须一致。
在Java中,散列表由链表数组实现,每个列表被称为桶。如果遇到散列冲突,需要用新对象与散列中的对象进行比较,查看对象是否已经存在。Java提供的HashSet类,实现了基于散列表的集合,contains方法只在某个桶中查找元素,而不必查看所有的元素。
问题:对于Java中的某个集合对象,如何判断一个值是否在这个集合中?判断时集合所采用的是equals方法还是contains方法?
针对这个问题,对大部分的Java集合中contains方法做了整理:
-
List集合:利用equals方法判断包含。
-
HashSet集合:利用hashCode和equals两个方法判断。通过hashCode定位到散列桶中,然后再利用equals方法判断是否存在。
-
TreeSet集合:被比较对象的类实现Comaprable接口或者在构造时传入Comparator接口的实现类。
-
HashMap集合:与HashSet相同。
-
TreeMap集合:与TreeSet相同。
附: Map中getEntry方法的实现:
final EntrygetEntry(Object key) { //得到key的hash code int hash = (key == null) ? 0 : hash(key.hashCode()); //遍历table里key的桶里的元素 for (Entry e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; //判断如果hash值相等并且是同一个对象或者对象相等测试为真 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null;}
参考文章:
[1]Java核心技术,卷I
[2].