为什么像HashMap / table这样的集合检查hashcode()for add / put并检查equals()for contains()

时间:2014-06-11 22:33:27

标签: java hashcode

我不明白为什么Map.put()只检查hashcode()和Map.containsKey()只检查equals()。

为什么不保持一致性。在两种情况下都检查哈希码或在两种情况下均等。

3 个答案:

答案 0 :(得分:3)

两个或多个对象可以返回相同的hashCode,但如果它们是equals,则它们只能相同。

Map#containsKey只解释了实现应使用equals方法来比较密钥。但这并不意味着实现无法使用hashCode来搜索可能所需键的键。这在HashMap#containsKey实施中已经注明。从Java 8的来源发布该方法的实现:

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

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;
}

请注意TreeMap不能与hashCode一起使用,而是使用键的自然比较。因此,在此实现中无需使用hashCode。请注意,TreeMap#containsKey更改了javadoc以解释它没有使用equals方法。从Java 8的源代码中显示此方法的源代码,它根本不使用equals(评论是我的):

public boolean containsKey(Object key) {
    return getEntry(key) != null;
}

final Entry<K,V> getEntry(Object key) {
    // Offload comparator-based version for sake of performance
    if (comparator != null)
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();
    @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
    Entry<K,V> p = root;
    //navigating through the nodes of the inner red black tree
    while (p != null) {
        //using result of compareTo method to check if the key is found
        //this replaces usage of equals method
        int cmp = k.compareTo(p.key);
        if (cmp < 0)
            p = p.left;
        else if (cmp > 0)
            p = p.right;
        else
            return p;
    }
    return null;
}

答案 1 :(得分:2)

这是因为hashCode()equals()之间的关系。如果两个对象相等,则它们必须具有相同的哈希码,但反向不一定是真的

因此,如果您想查看某个集合是否包含某些内容,那么不能使用hashCode()进行相等性检查,否则如果您碰巧有两个不相等的对象返回相同的数字hashCode(),你开始遇到重大问题。

此外,由于哈希表的工作方式,hashCode()更适用于补充 equals(),主要用于哈希集合。来自Object的javadoc:

  

public int hashCode()

     

返回对象的哈希码值。支持此方法是为了哈希表的优势,例如HashMap提供的哈希表。

哈希表通过计算对象的“哈希”来工作,在一些数学运算之后,它为后备数组提供索引。鉴于良好的hashCode()实现,这允许使用摊销的O(1)put()get()contains()和其他方法进行收集,这是非常理想的属性。

然而,这主要是一个便利功能。最后,地图必须仍然使用equals(),以确保您检索的对象实际上是您想要的对象。事实上,可以编写完全不使用Map的{​​{1}}实现,例如使用hashCode()的{​​{1}}。

答案 2 :(得分:0)

我得到了答案。

我们不能对add()使用contains(),因为当我们想要找到存储元素的位置/地址时,我们使用hashcode()并插入它。但是,在检查密钥是否已存在时,使用contains(),必须使用equals方法将现有元素与样本密钥进行比较。

现在我明白了。谢谢大家。