java 为什么在添加到 HashSet 和 hashCode 匹配时不调用 equals()?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25331926/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 07:50:47  来源:igfitidea点击:

Why is equals() not called while adding to HashSet and hashCode matches?

java

提问by san9w

When I run this code why only hashCode()is called not equalsmethod while my hashCode()implementation generate same hashCodefor both entries to HashSet?

当我运行此代码时,为什么只hashCode()调用 notequals方法,而我的hashCode()实现hashCode为两个条目生成相同的HashSet?

import java.util.HashSet;

public class Test1 {
    public static void main(String[] args) {
        Student st=new Student(89);
        HashSet st1=new HashSet();
        st1.add(st);
        st1.add(st);
        System.out.println("Ho size="+st1.size());
    }
}
class Student{
    private int name;
    private int ID;
    public Student(int iD) {
        super();
        this.ID = iD;
    }
    @Override
    public int hashCode() {
        System.out.println("Hello-hashcode");
        return ID;
    }
    @Override
    public boolean equals(Object obj) {
        System.out.println("Hello-equals");
        if(obj instanceof Student){
            if(this.ID==((Student)obj).ID){
                return true;
            }
            else{
                return false;
            }
        }
        return false;  
    }
}

The output for this is:

这个的输出是:

Hello-hashcode
Hello-hashcode
Ho size=1

采纳答案by that other guy

The hash set checks reference equality first, and if that passes, it skips the .equalscall. This is an optimization and works because the contract of equalsspecifies that if a == bthen a.equals(b).

哈希集首先检查引用相等性,如果通过,则跳过.equals调用。这是一种优化并且有效,因为合同equals指定 if a == bthen a.equals(b)

I attached the source code below, with this check highlighted.

我附上了下面的源代码,并突出显示了此检查。

If you instead add two equal elements that are not the same reference, you get the effect you were expecting:

如果您改为添加两个不同引用的相等元素,则会得到您期望的效果:

    HashSet st1=new HashSet();
    st1.add(new Student(89));
    st1.add(new Student(89));
    System.out.println("Ho size="+st1.size());

results in

结果是

$ java Test1
Hello-hashcode
Hello-hashcode
Hello-equals
Ho size=1


Here's the source code from OpenJDK 7, with equality optimization indicated (from HashMap, the underlying implementation of HashSet):

这是来自 OpenJDK 7 的源代码,指示了等式优化(来自 HashMap,HashSet 的底层实现):

public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
//                                         v-- HERE
        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;
}

回答by rgettman

A HashSetuses a HashMapas its backing mechanism for the set. Normally, we would expect that hashCodeand equalswould be called to ensure that there are no duplicates. However, the putmethod (which calls a privateputValmethod to do the actual operation) makes an optimization in the source code:

AHashSet使用 aHashMap作为集合的支持机制。通常,我们会期望hashCode并且equals会被调用以确保没有重复。但是put方法(调用一个privateputVal方法来做实际操作)在源码中做了优化:

if (e.hash == hash &&
    ((k = e.key) == key || (key != null && key.equals(k))))

If the hash codes match, it first checks to see if the keys are the same before calling equals. You are passing the same Studentobject, so they are already ==, so the ||operator short-circuits, and equalsis never called.

如果哈希码匹配,它首先检查键是否相同,然后再调用equals. 您正在传递相同的Student对象,因此它们已经是==,因此||运算符短路,并且equals永远不会被调用。

If you passed in a different Studentobject but with the same ID, then ==would return falseand equalswould be called.

如果您传入不同的Student对象但具有相同的ID==则将返回false并被equals调用。

回答by SamYonnou

Looking through the source code of HashSetit is using HashMapfor all of its operations and the add method performs put(element, SOME_CONSTANT_OBJECT). Here is the source code for the put method for JDK 1.6.0_17 :

查看HashSetHashMap的所有操作的源代码,并且 add 方法执行put(element, SOME_CONSTANT_OBJECT)。这是 JDK 1.6.0_17 的 put 方法的源代码:

public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key.hashCode());
    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;
}

as you can see it performs a ==comparison before using the equals method. Since you are adding the same instance of an object twice the ==returns true and the equals method is never called.

如您所见,它==在使用 equals 方法之前执行比较。由于您添加了相同的对象实例两次,因此==返回 true 并且永远不会调用 equals 方法。

回答by Abhi

Equals is always called after the hashCode method in a java hashed collection while adding and removing elements. The reason being, if there is an element already at the specified bucket, then JVM checks whether it is the same element which it is trying to put.

添加和删​​除元素时,始终在 java 散列集合中的 hashCode 方法之后调用 Equals。原因是,如果指定的存储桶中已经有一个元素,则 JVM 会检查它是否与它尝试放入的元素相同。

hashcode() and equals() method

hashcode() 和 equals() 方法

回答by Himanshu Sharma

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.

如果根据 equals(Object) 方法两个对象相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。