zoukankan      html  css  js  c++  java
  • 覆写equals方法为什么需要覆写hashCode方法

      覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢?

    void test() {
        // Person类的实例作为Map的key 
        Map<Person, Object> map = new HashMap<Person, Object>();
        map.put(new Person("张三"), new Object()); 
            
        // Person类的实例作为List的元素 
        List<Person> list = newArrayList<Person>()
        list.add(new Person("张三")); 
     
        // 列表中是否包含 
        boolean b1 = list.contains(new Person("张三")); 
        // Map中是否包含 
        boolean b2 = map.containsKey(new Person("张三"));
        System.out.println(b1 + "|" + b2);
    }

    Person类:

    public class Person {
        private String name;
        private String age;
       
        public Person(String name) {
            setName(name);
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAge() {
            return age;
        }
        public void setAge(String age) {
            this.age = age;
        }
       
        @Override
        public boolean equals(Object o) {
            /*
             * 使用getClass是为了防止子类被判断为父类的情况
             * 例如:Person中覆写equals方法 {return name.equals(p.getName);}
             *     子类Employee继承Person,此时子类中的euqals方法super.equlas(p);
             * 此时如果使用instanceof关键字,因为Employee是Person的子类,所以返回true
             *  进一步调用equals返回的是true,所以会将Person和Employee认为是同一人,显然不合理
             */
            if(o != null && o.getClass() == this.getClass()) {
                Person p = (Person) o;
                if(p.getName() == null && this.name == null)
                    return false;
                return this.name.equals(p.getName());
            }
            return false;
        }
    }

      我们先来看b1,Person类的equals覆写了,不再判断两个地址是否相等,而是根据人员的姓名来判断两个对象是否相等,所以不管我们的 new Person(“张三”)产生了多少个对象,它们都是相等的。把“张三”对象放入List中,再检查List中是否包含,那结果肯定是true了。

      接着来看b2,我们把张三这个对象作为了Map的键(Key),放进去的对象是张三,检查的对象还是张三,那应该和List的结果相同了,但是很遗憾,结果是false。原因何在呢?

      原因就是HashMap的底层处理机制是以数组的方式保存Map条目(Map Entry)的,这其中的关键是这个数组下标的处理机制:依据传入元素hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了Map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目,则插入,并加入到Map条目的链表中。同理,检查键是否存在也是根据哈希码确定位置,然后遍历查找键值的。

      接着深入探讨,那对象元素的hashCode方法返回的是什么值呢?它是一个对象的哈希码,是由Object类的本地方法生成的,确保每个对象有一个哈希码(这也是哈希算法的基本要求:任意输入k,通过一定算法f(k),将其转换为非可逆的输出,对于两个输入k1和k2,要求若k1=k2,则必须 f(k1)=f(k2),但也允许k1≠k2,f(k1)=f(k2)的情况存在)。

      那回到我们的例子上,由于我们没有重写hashCode方法,两个张三对象的hashCode方法返回值(也就是哈希码)肯定是不相同的了,在HashMap的数组中也就找不到对应的Map条目了,于是就返回了false。

      问题清楚了,修改也非常简单,重写一下hashCode方法即可,代码如下:

    public int hashCode() {
         //其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,
         //使用起来非常方便,诸位可以直接在项目中集成。
         return new HashCodeBuilder().append(name).toHashCode();
    }

      其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,使用起来非常方便,诸位可以直接在项目中集成。(为什么不直接写hashCode方法?因为哈希码的生成有很多种算法,自己写麻烦,事儿又多,所以采用拿来主义是最好的方法。)

  • 相关阅读:
    第七周学习进度总结
    软件需求分析阅读笔记3
    第二周课堂小测:判断数组的最大子数组
    Android记账本开发(四):使用第三方插件库完成饼图数据显示
    Android记账本开发(三):数据库开发
    Android记账本开发(二):实现注册登录功能
    Android记账本开发(一):整体UI界面布局
    Android:Handle与Service
    上篇博客简单实例:字典与通讯录
    Android:数据存储技术
  • 原文地址:https://www.cnblogs.com/wangmingshun/p/5398501.html
Copyright © 2011-2022 走看看