关于Object类的hashCode方法

####关于Object类的hashCode方法 //2016-08-19 16:00 在Object类的文档上是这么说hashcode()的.

https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()

The general contract of hashCode is: 1.Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.

2.If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

3.It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

1.无论什么时候一个对象的hashcode方法被调用多少次, 只要那些被用于equals比较的信息没有改变.那么就应该是同样的结果.

2.如果两个对象通过***equals被认为相等,那么他们的hashcode也必须相等.***

3.如果两个对象被认为不equal,那么他们的hashCode***不用非得不相等***.


  • 第一条没什么可说的,但是第二三条却又是为什么呢?
  • 好比无序的Set集合是怎么知道内部元素有没有重复的呢?

通过看代码可以发现HashSet其实内部实现就是一个HashMap. 这里看一下contains方法的实现.

//java.util.HashSet.java 

transient HashMap<E, HashSet<E>> backingMap;

 @Override
 public boolean contains(Object object) {
     return backingMap.containsKey(object);
 }

//java.util.HashMap.java
Override public boolean containsKey(Object key) {
    if (key == null) {
        return entryForNullKey != null;
    }

    int hash = Collections.secondaryHash(key);
    HashMapEntry<K, V>[] tab = table;
    for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
            e != null; e = e.next) {
        K eKey = e.key;
        if (eKey == key || (e.hash == hash && key.equals(eKey))) {
            return true;
        }
    }
    return false;
}

大致来说就是HashMap通过key的hashCode值来确定这个元素在内部存储的位置.

那么问题来了,根据文档上的规定***两个对象equals相等必须hashcode也相等***. 如果现在像这个情况,对象的equals相同但是hashcode不同会怎么样?

A.equals(B) && A.hashCode() != B.hashCode()

因为A.equals(B)你就认为A和B是一样的了.

map.put(A)
map.get(A)
map.get(B)
//java.lang.Object.java
   
 public int hashCode() {
     int lockWord = shadow$_monitor_;
     final int lockWordMask = 0xC0000000;  // Top 2 bits.
     final int lockWordStateHash = 0x80000000;  // Top 2 bits are value 2 (kStateHash).
     if ((lockWord & lockWordMask) == lockWordStateHash) {
         return lockWord & ~lockWordMask;
     }
     return System.identityHashCode(this);
 }

// java.lang.System.java
   public static native int identityHashCode(Object anObject);

注意如果你没重写对象的hashCode方法,那么它就是直接继承自Object类, 那么这里的map.get(B)就会返回null. 但是你明明已经put进map了(因为A.equals(B)) 所以为了避免这种蛋疼的情况, 你就应该保证两个对象equals相等那么hashcode也应该相等.

***如果两个对象被认为不equal,那么他们的hashCode不用非得不相等. ***

反过来看,即两个不equals的对象但是hashCode相同了. 也就是所谓的的hash碰撞. 下面以HashMap的代码来看看java是怎么处理这个问题的.

    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }

    /**
     * Implements Map.get and related methods
     *
     * [@param](https://my.oschina.net/u/2303379) hash hash for key
     * [@param](https://my.oschina.net/u/2303379) key the key
     * [@return](https://my.oschina.net/u/556800) the node, or null if none
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

static class Node<K,V> implements Map.Entry<K,V> {
	final int hash;
	final K key;
	V value;
	Node<K,V> next;
}

可以看到Node是一个单链表,如果发生hash碰撞了就需要挨个遍历. 但是如果没有hash碰撞,就可以直接返回. 所以应该尽可能的保证两个对象不一样,hashcode也不一样.

转载于:https://my.oschina.net/tanghaoo/blog/1842784

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值