最近我读了这篇文章 开发人员工作文件。

该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。

我如何决定有效地实现这些方法?


当前回答

如果重写equals()而不是hashcode(),则不会发现任何问题,除非您或其他人在HashSet等散列集合中使用该类类型。 在我之前的人已经清楚地解释了很多次文献理论,我只是在这里提供一个非常简单的例子。

考虑一个类,它的equals()需要表示自定义的东西:-

    public class Rishav {

        private String rshv;

        public Rishav(String rshv) {
            this.rshv = rshv;
        }

        /**
        * @return the rshv
        */
        public String getRshv() {
            return rshv;
        }

        /**
        * @param rshv the rshv to set
        */
        public void setRshv(String rshv) {
            this.rshv = rshv;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Rishav) {
                obj = (Rishav) obj;
                if (this.rshv.equals(((Rishav) obj).getRshv())) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return rshv.hashCode();
        }

    }

现在考虑这个主类:-

    import java.util.HashSet;
    import java.util.Set;

    public class TestRishav {

        public static void main(String[] args) {
            Rishav rA = new Rishav("rishav");
            Rishav rB = new Rishav("rishav");
            System.out.println(rA.equals(rB));
            System.out.println("-----------------------------------");

            Set<Rishav> hashed = new HashSet<>();
            hashed.add(rA);
            System.out.println(hashed.contains(rB));
            System.out.println("-----------------------------------");

            hashed.add(rB);
            System.out.println(hashed.size());
        }

    }

这将产生以下输出:-

    true
    -----------------------------------
    true
    -----------------------------------
    1

我对结果很满意。但是如果我没有覆盖hashCode(),它将导致噩梦,因为具有相同成员内容的Rishav对象将不再被视为唯一的hashCode将是不同的,因为由默认行为生成,这里将是输出:-

    true
    -----------------------------------
    false
    -----------------------------------
    2

其他回答

身份不是平等。

= operator ==测试标识符。 equals(Object obj)方法比较相等性测试(即我们需要通过重写方法来告诉相等)

为什么我需要重写Java中的equals和hashCode方法?

首先我们要理解等号法的用法。

为了识别两个对象之间的差异,我们需要重写equals方法。

例如:

Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.

------------------------------
Now I have overriden Customer class equals method as follows:
 @Override
    public boolean equals(Object obj) {
        if (this == obj)   // it checks references
            return true;
        if (obj == null) // checks null
            return false;
        if (getClass() != obj.getClass()) // both object are instances of same class or not
            return false;
        Customer other = (Customer) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference 
            return false;
        return true; 
    }
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2);  // returns true by our own logic

现在hashCode方法很容易理解了。

hashCode生成整数,以便将对象存储在HashMap、HashSet等数据结构中。

假设我们有如上所述的override equals Customer方法,

customer1.equals(customer2);  // returns true by our own logic

在处理数据结构时,我们将对象存储在桶中(桶是文件夹的花哨名称)。如果我们使用内置哈希技术,对于以上两个客户,它会生成两个不同的哈希码。所以我们把相同的对象存储在两个不同的地方。为了避免这类问题,我们也应该基于以下原则重写hashCode方法。

不相等的实例可能具有相同的hashcode。 相同的实例应该返回相同的hashcode。

class A {
    int i;
    // Hashing Algorithm
    if even number return 0 else return 1
    // Equals Algorithm,
    if i = this.i return true else false
}

put('key','value')将使用hashCode()计算哈希值来确定 桶,并使用equals()方法查找该值是否已经 出现在桶里。如果不是,它将被添加,否则它将被替换为当前值 get('key')将使用hashCode()首先找到条目(桶) equals()来查找Entry中的值

如果两者都被覆盖,

地图<A>

Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...

If =没有被覆盖

地图<A>

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..

如果hashCode没有被覆盖

地图<A>

Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...

HashCode等价契约

根据equal方法,两个相等的键应该生成相同的hashCode 生成相同hashCode的两个key不需要相等(在上面的例子中,所有偶数生成相同的hashCode)

我正在研究解释“如果你只覆盖hashCode,那么当你调用myMap.put(first,someValue)时,它首先接受,计算它的hashCode并将其存储在给定的桶中。然后,当你调用myMap.put(first,someOtherValue)时,它应该根据Map文档将first替换为second,因为它们是相等的(根据我们的定义)。”:

我认为第二次添加myMap时应该是第二个对象比如myMap。put(second,someOtherValue)

hashCode ():

如果只重写hash-code方法,什么也不会发生,因为它总是为每个对象返回一个新的hashCode作为object类。

equals ():

如果你只覆盖equals方法,如果a.equals(b)为真,这意味着a和b的hashCode必须是相同的,但这不会发生,因为你没有覆盖hashCode方法。

注意:Object类的hashCode()方法总是为每个对象返回一个新的hashCode。

因此,当您需要在基于哈希的集合中使用对象时,必须重写equals()和hashCode()。

hashCode()方法用于获取给定对象的唯一整数。这个整数用于确定桶的位置,当这个对象需要存储在一些HashTable, HashMap之类的数据结构时。默认情况下,Object的hashCode()方法返回存储Object的内存地址的整数表示形式。

对象的hashCode()方法用于将对象插入到HashTable、HashMap或HashSet中。更多关于哈希表的信息请参见Wikipedia.org。

要在map数据结构中插入任何条目,我们需要键和值。如果键和值都是用户定义的数据类型,则键的hashCode()将确定在内部将对象存储在何处。当还需要从映射中查找对象时,键的哈希码将确定在哪里搜索对象。

哈希码只在内部指向某个“区域”(或列表,桶等)。因为不同的键对象可能具有相同的哈希码,所以哈希码本身并不能保证找到正确的键。然后哈希表迭代该区域(具有相同哈希代码的所有键),并使用键的equals()方法找到正确的键。一旦找到正确的键,就会返回为该键存储的对象。

因此,正如我们所看到的,hashCode()和equals()方法的组合在哈希表中存储和查找对象时使用。

注:

总是使用对象的相同属性来生成hashCode()和equals()。在我们的例子中,我们使用了员工id。 Equals()必须是一致的(如果对象没有被修改,那么它必须一直返回相同的值)。 当a. = (b)时,则a.hashCode()必须与b. hashcode()相同。 如果你覆盖了一个,那么你也应该覆盖另一个。

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html