Java类的Hash函数和集合类

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 Entry<K,V> getEntry(Object key) {
    //得到key的hash code
    int hash = (key == null) ? 0 : hash(key.hashCode());
    //遍历table里key的桶里的元素
    for (Entry<K,V> 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]创想.

posted @   MrLocker  阅读(1511)  评论(0)    收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示