为什么覆写equals的时候一定要覆写hashCode

经常在论坛上面看到覆写hashCode函数的问题,很多情况下是一些开发者不了解hash code,或者和equals一起用的时候不太清楚为啥一定要覆写hashCode。 

    对于hash code的理论我不想多说,这个话题太大。那些“对称性”,“传递性”的规则网上有无数的文章来描述。我只想说用hash code的原因只有一个:效率。理论的说法它的复杂度只有O(1)。试想我们把元素放在线性表里面,每次要找一个元素必须从头一个一个的找它的复杂度有O(n)。如果放在平衡二叉树,复杂度也有O(log n)。 

   为啥很多地方说“覆写equals的时候一定要覆写hashCode”。说到这里我知道很多人知道有个原则:如果a.equals(b)那么要确保a.hashCode()==b.hashCode()。为什么?hashCode和我写的程序的业务逻辑毫无关系,为啥我要override? 要我说如果你的class永远不可能放在hash code为基础的容器内,不必劳神,您真的不必override hashCode() 

    说得准确一点放在HashMap和Hashtable里面如果是作为value而不是作为key的话也是不必override hashCode了。至于HashSet,实际上它只是忽略value的HashMap,每次HashSet.add(o)其实就是HashMap.put(o, dummyObject)。 

    那为什么放到Hash容器里面要overide hashCode呢?因为每次get的时候HashMap既要看equals是不是true也要看hash code是不是一致,put的时候也是要看equals和hash code。 

    如果说到这里您还是不太明白,咱就举个例子: 

    譬如把一个自己定义的class Foo{...}作为key放到HashMap。实际上HashMap也是把数据存在一个数组里面,所以在put函数里面,HashMap会调Foo.hashCode()算出作为这个元素在数组里面的下标,然后把key和value封装成一个对象放到数组。等一下,万一2个对象算出来的hash code一样怎么办?会不会冲掉?先回答第2个问题,会不会冲掉就要看Foo.equals()了,如果equals()也是true那就要冲掉了。万一是false,就是所谓的collision了。当2个元素hashCode一样但是equals为false的时候,那个HashMap里面的数组的这个元素就变成了链表。也就是hash code一样的元素在一个链表里面,链表的头在那个数组里面。 

回过来说get的时候,HashMap也先调key.hashCode()算出数组下标,然后看equals如果是true就是找到了,所以就涉及了equals。 

    假设如果有个key为a的元素在HashMap里面的情况: 

    1:如果这时候用equals为true但是hashCode不等的b作为get参数的话,这个时候b算出来的数组下标一定不是a所在的下标位置。 

    2:如果这时候用equals为false但是hashCode相等的b作为get参数的话,这个时候b算出来的数组下标是对了,但是用equals来寻找相符的key就找不到a了。 

    以上2种情况要么就是get找不到符合的元素返回null,要么就是返回一个hashCode和equals恰好都符合b的另外的元素,这就产生了混乱。混乱的根本就是错误实现hashCode和equals。 

  下面有个非常简化版的HashMap实现帮助大家理解,您甚至可以把它当作伪代码来看。(这个实现只是为了模拟HashMap的机制,所以参数检查,访问修饰符都忽略。在java.util.HashMap里面的对hash值的二次hash,根据数组长度计算index,以及超出数组时的resize都忽略) 

view plaincopy to clipboardprint?
/*  
 * Just to demonstrate hash map mechanism,   
 * Please do not use it in your commercial product.  
 *   
 * Argument checking and modifier are ignored.  
 * In java.util.HashMap, array are used instead of ArrayList, so index of array is calculated by 'h & (length-1)',  
 * while we use ArrayList to skip the calculation for simple.  
 *   
 * @author Shengyuan Lu 卢声远 <michaellufhl@yahoo.com.cn>  
 */  
public class SimpleHashMap {   
    ArrayList<LinkedList<Entry>> entries = new ArrayList<LinkedList<Entry>>();   
       
    /**  
     * Each key-value is encapsulated by Entry.  
     */  
    static class Entry {   
        Object key;   
        Object value;   
        public Entry(Object key, Object value) {   
            this.key = key;   
            this.value = value;   
        }   
    }   
    void put(Object key, Object value) {   
        LinkedList<Entry> e = entries.get(key.hashCode());   
        if (e != null) {   
            for (Entry entry : e) {   
                if (entry.key.equals(key)) {   
                    entry.value = value;// Match in lined list   
                    return;   
                }   
            }   
            e.addFirst(new Entry(key, value));// Add the entry to the list   
        } else {   
            // Put the new entry in array   
            LinkedList<Entry> newEntry = new LinkedList<Entry>();   
            newEntry.add(new Entry(key, value));   
            entries.add(key.hashCode(), newEntry);   
        }   
    }   
    Object get(Object key) {   
        LinkedList<Entry> e = entries.get(key.hashCode());   
        if (e != null) {   
            for (Entry entry : e) {   
                if (entry.key.equals(key)) {   
                    return entry.value;   
                }   
            }   
        }   
        return null;   
    }   
       
    /**  
     * Do we need to override equals() and hashCode() for SimpleHashMap itself?   
     * I don't know either:)  
     */  
}  

自己的理解:

第一个问题:这里需要解释为什么equals()相等,hashCode就一定要相等,而hashCode相等,却不要求equals相等?

其实这很好理解:主要从Hash的数据结构来解释就OK了,equals相等,意味着两个对象在Hash的存储中(采用链表法解决冲突问题),存放在同一个位置,这就要求两个对象必须在数组的同一个index中。而index = (hashCode() % MODULE ) 得来的。故其HashCode必须相等。而反过来,两个HashCode相等,只是表明两个对象在数组的index 相同,但其可能是产生了冲突,这里采用链表法来解决,所以其在同一个index中,但在链表的不同位置上。

 

第二:为什么hashCode与Equals要同时实现呢?且实现的算法所利用的成员要一致呢?

假定你的Equals是基于内容来实现的。而你的HashCode不变,还是继承于Object,那么当两个对象相等时,即Equals相等,而你实现的HashCode不等,这就违反了第一个问题的规定。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值