java通用方法equals、hashcode的重写注意事项

最近在读effictive java这本书,看到关于java通用方法重写时的某些规则,又想起项目中重写bean的equals方法,仔细一想确实有很多不正确的地方,所幸项目中的那个对象使用频率低没有出现问题。结合看hashmap的源码时发现读取写入方法与equals,hashcode密切相关,为了不秒忘记,在这记录一下。

equals和hashcode本来都只是object的方法,如果不进行重写,那么equals源码实际上是==比较,比较的是两个对象的地址,除非是同一个对象,否则均为false.

例:public boolean equals(Object obj) {
        return (this == obj);
    }
    而我们经常使用的类库,如String、Character等都是对其进行重写后,有自己相等的逻辑,如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;
    }
 hashcode也是如此,object的hashcode返回的也是和对象地址相关的值,而重写时是根据类的字段有很多规则,在此暂时不深究。但hashcode与equals之间有几个约定规则。
    1.如果两个对象通过equals方法判定相等,则他们的hashcode必须相等。
    2.反之,如果两个对象有相同的hashcode,却并不一定equals。
    3.对同一个对象,无论其内部属性如何改变,应该返回相同的hashcode。
    一开始我不太理解这些规则,我想我自己在写一个类时即使不这么做你又能耐我何呢?...
    知道我在使用集合类库时得不到正确的结果时我懂了,其实这都是为集合类库做准备,因为我们平时一般操作多个对象都是通过集合类库啊。比如想查看下某个对象的Index
public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
 显然是用equals查找对象,如果没有重写对象的equals方法,就会调用父类object的equals方法,即为==比较,如果new people("zhangshan"),然后再indexOf(new people("zhangshan"))则会返回-1似乎找不到zhangshan这个刚刚加进去的对象。去年刚毕业时面试似乎还被问到这个问题,当时还不服的觉得是混淆视听,其实是对基本方法的理解不够。
    在说hashcode,其实是为hash算法而生,诸如hashset,hashmap,hashtable等,其实看他们的源码会理解的十分深刻。
public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }


        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }
 首先判断是根据key的hashcode生成hash值,然后根据这个值和数组长度得到其数组下表,即其即将要插入的位置。这里其实包含了很多东西,首先hashcode如果为了方便都返回一样的值,那么所有的键值对都将装在一个桶中,hashmap即退化为普通链表。而如果对象的hashcode不满足上面的第一点,即相同对象hashcode却不同则会导致其在桶中的位置不同,即会造成重复的键,已经不符合map的原则。而hashcode由于是由计算产生,尽管算法尽量避免重复,但当然有重复的可能,所以并不能由此说明两对象equals,此时则会在桶中的相同位置即链表中加入数据。对于第三点是显而易见,如果一个对象仅仅是由于属性值发生变化,hashcode就发生变化,那就根本无法确定它作为键时在桶里位置,也就根本找不到对应的值了,都没机会做equals比较了,如下。
final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }


        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值