为什么我需要覆盖 Java 中的 equals 和 hashCode 方法?

Why do I need to override the equals and hashCode methods in Java?

提问人:Shashi 提问时间:2/15/2010 最后编辑:ExploringShashi 更新时间:3/14/2023 访问量:630909

问:

最近,我通读了这个开发者作品文档

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

我怎样才能决定有效地实施这些方法?

Java 等于 哈希码

评论

1赞 VedantK 9/12/2017
大小写覆盖仅等于:两个相同的对象将具有不同的哈希码 = 相同的对象进入不同的存储桶(重复)。案例仅覆盖哈希码:两个相同的对象将具有相同的哈希码 = 相同的对象进入同一个存储桶(重复)。
0赞 tahasozgen 6/18/2021
该链接似乎已失效。我能获得 IBM 的开发人员作品文档吗?

答:

7赞 pauljwilliams 2/15/2010 #1

因为如果你不覆盖它们,你将使用 Object 中的默认实现。

鉴于实例相等和 hascode 值通常需要了解构成对象的内容,因此通常需要在类中重新定义它们才能具有任何有形的含义。

4赞 Dewfy 2/15/2010 #2

假设您有聚合另外两个 (B) (C) 的类 (A),并且您需要将 (A) 的实例存储在哈希表中。默认实现只允许区分实例,但不允许通过 (B) 和 (C) 进行区分。因此,A 的两个实例可能相等,但默认值不允许你以正确的方式比较它们。

5赞 Ionuț G. Stan 2/15/2010 #3

它在使用值对象时很有用。以下是 Portland Pattern Repository 的摘录:

值对象的例子是事物 喜欢数字、日期、金额和 字符串。通常,它们很小 使用相当广泛的对象。 他们的身份基于他们的状态 而不是他们的对象身份。 这样,您可以拥有多个副本 相同的概念值对象。

所以我可以拥有多个副本 表示日期 1 月 16 日的对象 1998. 这些副本中的任何一个都将彼此相等。对于小 像这样的对象,它通常是 更容易创建新和移动 他们周围而不是依赖 表示日期的单个对象。

值对象应始终覆盖 Java 中的 .equals() (或 Smalltalk 中的 =)。 (记得将 .hashCode() 覆盖为 好吧。

56赞 JuanZe 2/15/2010 #4

您必须在每 覆盖 equals() 的类。失败 这样做将导致违反 总承包 Object.hashCode(),这将阻止 你的班级无法正常运行 结合所有基于哈希的 集合,包括 HashMap、 HashSet 和 Hashtable。


摘自 Joshua Bloch 的 Effective Java

通过定义和一致地定义类,可以提高类作为基于哈希的集合中的键的可用性。正如 hashCode 的 API 文档所解释的那样:“支持此方法是为了哈希表(例如由 提供的哈希表)的好处。equals()hashCode()java.util.Hashtable

关于如何有效地实现这些方法的问题,最好的答案是建议你阅读《有效 Java》的第 3 章。

评论

5赞 slim 10/15/2015
这是正确答案。当然,推论是,如果您从未在基于哈希的集合中使用该类,那么您没有实现 .hashCode()
2赞 Victor Sergienko 3/20/2016
在更复杂的情况下,你永远不知道你使用的集合是否在使用哈希,所以远离“你没有实现hashCode()并不重要”
1赞 Johnny 4/4/2016
我可以在不覆盖equals()的情况下覆盖hashCode()吗?
0赞 Gi1ber7 3/12/2021
@Johnny当然,您可以在不覆盖等值的情况下覆盖 hascode。但是用例是什么?
0赞 Panagiotis Bougioukos 10/6/2021
@Gi1ber7从这里开始检查我的答案,以分析性地了解 HashMap 和 HashTable 发生了什么。equalshashCode
0赞 GuruKulki 2/15/2010 #5

这两种方法都在 Object 类中定义。两者都处于最简单的实现状态。因此,当您需要时,您想为这些方法添加更多实现,然后您可以在类中覆盖。

对于 Ex: 对象中的 equals() 方法仅检查其在引用上的相等性。因此,如果您还需要比较其状态,则可以像在 String 类中一样覆盖它。

26赞 crunchdog 2/15/2010 #6

简单地说,Object 中的 equals-method 检查引用是否相等,当属性相等时,类的两个实例在语义上仍然可以相等。例如,当将对象放入使用等于和哈希码的容器(如 HashMapSet)时,这一点很重要。假设我们有一个这样的类:

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }
}

我们创建两个具有相同 ID 的实例:

Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");

在不覆盖等价物的情况下,我们得到:

  • a.equals(b) 为 false,因为它们是两个不同的实例
  • a.equals(a) 为 true,因为它是同一个实例
  • b.equals(b) 为 true,因为它是同一个实例

正确?好吧,也许,如果这是你想要的。但是,假设我们希望具有相同 id 的对象是同一个对象,而不管它是否是两个不同的实例。我们覆盖等号(和哈希码):

public class Foo {
    String id;
    String whatevs;

    Foo(String id, String whatevs) {
        this.id = id;
        this.whatevs = whatevs;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Foo) {
            return ((Foo)other).id.equals(this.id);   
        }
    }

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

至于实现等于和哈希码,我可以推荐使用 Guava 的辅助方法

700赞 Lombo 2/15/2010 #7

Joshua Bloch 在《Effective Java》一书中说道

您必须在覆盖 equals() 的每个类中覆盖 hashCode()。如果不这样做,将导致违反 Object.hashCode() 的一般协定,这将阻止您的类与所有基于哈希的集合(包括 HashMap、HashSet 和 Hashtable)一起正常运行。

让我们试着用一个例子来理解它,如果我们在不覆盖的情况下覆盖并尝试使用 .equals()hashCode()Map

假设我们有一个这样的类,并且两个对象相等,如果它们相等(由 eclipse 生成)MyClassimportantFieldhashCode()equals()

public class MyClass {
    private final String importantField;
    private final String anotherField;

    public MyClass(final String equalField, final String anotherField) {
        this.importantField = equalField;
        this.anotherField = anotherField;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((importantField == null) ? 0 : importantField.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        final MyClass other = (MyClass) obj;
        if (importantField == null) {
            if (other.importantField != null)
                return false;
        } else if (!importantField.equals(other.importantField))
            return false;
        return true;
    }
}

想象一下你有这个

MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");

仅覆盖等于

如果只是被覆盖,那么当你第一次调用时,它将散列到某个存储桶,当你调用它时,它将散列到其他一些存储桶(因为它们有不同的)。因此,尽管它们是相等的,但由于它们不会散列到同一个存储桶,因此地图无法实现它,并且它们都留在地图中。equalsmyMap.put(first,someValue)myMap.put(second,someOtherValue)hashCode


虽然如果我们覆盖就没有必要覆盖,但让我们看看在这种特殊情况下会发生什么,如果我们知道两个对象相等,但它们相等,但我们不覆盖。equals()hashCode()MyClassimportantFieldequals()

仅覆盖 hashCode

如果只覆盖,则在调用时,首先计算它并将其存储在给定的存储桶中。然后,当您调用它时,应根据地图文档将其替换为第二个,因为它们是相等的(根据业务需求)。hashCodemyMap.put(first,someValue)hashCodemyMap.put(second,someOtherValue)

但问题是 equals 没有被重新定义,所以当 map 散列并遍历存储桶时,看看是否存在一个 true 的对象,它不会像现在这样找到任何对象。secondksecond.equals(k)second.equals(first)false

希望清楚

评论

7赞 Hussain Akhtar Wahid 'Ghouri' 5/6/2014
你能不能再详细说明一下,在第二种情况下,为什么第二个对象必须放在另一个桶里?
84赞 user2543253 12/30/2014
我不喜欢这个答案,因为它表明你不能在不覆盖equals()的情况下覆盖hashCode(),这根本不是真的。你说你的示例代码(“仅覆盖哈希代码”部分)不起作用,因为你将两个对象定义为相等的,但是 - 对不起 - 这个定义只在你的脑海中。在你的第一个例子中,你有两个不相等的对象,具有相同的hashCode,这是完全合法的。因此,你需要重写equals()的原因不是因为你已经重写了hashCode(),而是因为你想把你的“equals”定义从你的头脑转移到代码中。
23赞 akhil_mittal 10/17/2015
if you think you need to override one, then you need to override both of them是错误的。如果您的类覆盖但 reverse 不为 true,则需要覆盖。hashCodeequals
9赞 Johnny 4/4/2016
我认为只覆盖hashCode()而不覆盖equals()是完全可以的。这也是用 Effective Java 编写的内容:books.google.fr/...
3赞 aioobe 11/27/2016
@PhantomReference,请注意,只有覆盖才会违反 javadoc 中规定的契约:“如果根据 equals(Object) 方法,两个对象相等,那么对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。当然,并非所有合同的所有部分都在所有代码中执行,但从形式上讲,这仍然是一种违规行为,我认为这是一个等待发生的错误。equalsObject
4赞 Narinder 11/12/2010 #8

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

我认为第二次当我们添加时,它应该是“第二个”对象,例如myMapmyMap.put(second,someOtherValue)

3赞 Aarti 7/28/2013 #9

equals 和 hashcode 方法在对象类中定义。默认情况下,如果 equals 方法返回 true,则系统将进一步检查哈希代码的值。如果 2 个对象的哈希码也相同,则这两个对象将被视为相同。因此,如果仅覆盖 equals 方法,那么即使 overridden equals 方法指示 2 个对象相等,系统定义的哈希码也可能不指示 2 个对象相等。因此,我们也需要覆盖哈希代码。

评论

0赞 supercat 7/29/2013
如果 equals 方法返回 true,则无需检查哈希码。但是,如果两个对象具有不同的哈希码,则应该能够将它们视为不同的哈希码,而不必调用相等值。此外,知道列表中的任何事物都没有特定的哈希代码意味着列表中的任何事物都不能将任何对象与该哈希代码匹配。举个简单的例子,如果有一个哈希码为偶数的对象列表,以及一个为奇数的对象列表,则哈希码为偶数的对象不会出现在第二个列表中。
0赞 supercat 7/29/2013
如果有两个对象 X 和 Y,它们的“相等”方法表明它们匹配,但 X 的哈希代码是偶数,而 Y 的哈希代码是奇数,则如上所述的集合注意到对象 Y 的哈希代码是奇数并将其存储在第二个列表中将无法找到对象 X 的匹配项。它会观察到 X 的哈希码是偶数,并且由于第二个列表没有任何具有偶数哈希码的对象,因此即使 Y 与 X 匹配,它也不会费心在那里搜索与 X 匹配的内容。你应该说什么...
0赞 supercat 7/29/2013
...将是许多集合将避免比较哈希码暗示它们不能相等的事物。给定两个哈希码未知的对象,直接比较它们通常比计算它们的哈希码更快,因此不能保证报告不相等哈希码但返回的事物不会被视为匹配。另一方面,如果集合发生时注意到事物不能具有相同的哈希代码,他们可能不会注意到它们是相等的。trueequals
15赞 Rinkal Gupta 7/29/2013 #10

hashCode():

如果只重写哈希代码方法,则不会发生任何事情,因为它始终为每个对象返回一个新的对象作为 Object 类。hashCode

equals():

如果仅重写 equals 方法,则为 true,则表示 a 和 b 必须相同,但这不会发生,因为您没有重写该方法。a.equals(b)hashCodehashCode

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

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

评论

0赞 Johnny 4/4/2016
这是有趣的一点,关于仅覆盖 hashCode()。完全没问题,对吧?或者也会有问题的情况吗?
2赞 mfaisalhyder 6/24/2017
这是一个误导和错误的答案。覆盖 (=only=) hashCode() 确保每个被实例化的具有相似属性的相应类的对象都具有相同的哈希代码。但不会有用,因为它们都不会彼此相等。
12赞 Ritesh Kaushik 1/1/2014 #11

Java 设置了一条规则

“如果使用 Object class equals 方法两个对象相等,则哈希码方法应为这两个对象提供相同的值。”

因此,如果在我们的类中我们覆盖,我们也应该覆盖方法以遵循此规则。 例如,方法和 都用于将值存储为键值对。如果我们覆盖一个而不是另一个,如果我们使用这样的对象作为键,则可能无法按我们想要的方式工作。equals()hashcode()equals()hashcode()HashtableHashtable

6赞 Prashanth 3/11/2014 #12

为了使用我们自己的类对象作为 HashMap、Hashtable 等集合中的键。,我们应该通过了解收集的内部工作来覆盖这两个方法( hashCode() 和 equals() )。否则,它会导致我们意想不到的错误结果。

2赞 Paramesh Korrakuti 10/11/2014 #13

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

当我们将对象插入 、 或 时,会使用对象的方法。有关 Wikipedia.org 的更多信息,以供参考。hashCode()HashTableHashMapHashSetHashTables

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

哈希代码在内部仅指向某个“区域”(或列表、存储桶等)。由于不同的密钥对象可能具有相同的哈希代码,因此哈希代码本身并不能保证找到正确的密钥。然后迭代此区域(所有键都具有相同的哈希码),并使用键的方法找到正确的键。找到正确的键后,将返回为该键存储的对象。HashTableequals()

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

笔记:

  1. 始终使用对象的相同属性来生成和/或。在我们的案例中,我们使用了员工 ID。hashCode()equals()

  2. equals()必须一致(如果对象未修改,则它必须继续返回相同的值)。

  3. 每当 时,则必须与 相同。a.equals(b)a.hashCode()b.hashCode()

  4. 如果覆盖一个,则应覆盖另一个。

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

评论

0赞 user207421 6/17/2017
hashCode()不用于为每个对象返回一个唯一的整数。那是不可能的。你自己在第四段的第二句话中反驳了这一点。
0赞 Paramesh Korrakuti 9/18/2017
@EJP,大多数情况下 hascode() 将返回两个不同对象的唯一 interger。但是对于两个不同的对象,可能会发生冲突,这个概念被称为哈希码冲突。请参考 : tech.queryhome.com/96931/...
372赞 rajeev pani.. 11/27/2014 #14

集合(例如 和)使用对象的哈希码值来确定应如何将其存储在集合中,并再次使用哈希码来定位对象 在其收藏中。HashMapHashSet

哈希检索过程分为两步:

  1. 找到合适的存储桶(使用hashCode())
  2. 在存储桶中搜索正确的元素(使用equals() )

这里有一个小例子,说明为什么我们应该覆盖 和 。equals()hashcode()

考虑一个包含两个字段的类:age 和 name。Employee

public class Employee {

    String name;
    int age;

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;
        if (!(obj instanceof Employee))
            return false;
        Employee employee = (Employee) obj;
        return employee.getAge() == this.getAge()
                && employee.getName() == this.getName();
    }

    // commented    
    /*  @Override
        public int hashCode() {
            int result=17;
            result=31*result+age;
            result=31*result+(name!=null ? name.hashCode():0);
            return result;
        }
     */
}

现在创建一个类,将对象插入到一个中,并测试该对象是否存在。EmployeeHashSet

public class ClientTest {
    public static void main(String[] args) {
        Employee employee = new Employee("rajeev", 24);
        Employee employee1 = new Employee("rajeev", 25);
        Employee employee2 = new Employee("rajeev", 24);

        HashSet<Employee> employees = new HashSet<Employee>();
        employees.add(employee);
        System.out.println(employees.contains(employee2));
        System.out.println("employee.hashCode():  " + employee.hashCode()
        + "  employee2.hashCode():" + employee2.hashCode());
    }
}

它将打印以下内容:

false
employee.hashCode():  321755204  employee2.hashCode():375890482

现在取消注释方法,执行相同的方法,输出将是:hashcode()

true
employee.hashCode():  -938387308  employee2.hashCode():-938387308

现在你能明白为什么如果两个对象被认为是相等的,它们的哈希码必须 也是平等的吗?否则,您将永远无法找到该对象,因为 Object 类中的默认哈希码方法几乎总是带有一个唯一的编号 对于每个对象,即使该方法以 2 或多个对象被视为相等。如果对象相等,则无关紧要 他们的哈希码没有反映这一点。所以再说一遍:如果两个对象相等,它们的哈希码也必须相等。equals()

评论

0赞 Vikas Verma 3/13/2017
@rajeev我有一个困惑,为什么在 HashMap 的情况下重写 hashCode 方法时需要重写 equals 方法?在任何情况下,如果对象的哈希码相等,hashmap 都会替换该值。
6赞 Ravi 12/12/2017
@VikasVerma等于对象将具有相等的哈希码并不意味着不相等的对象将具有不相等的哈希码。如果对象实际上不同,但它们的哈希码是相同的怎么办?
2赞 JavaYouth 1/29/2019
有什么理由在hashcode()实现中使用这些数字(17和31)来生成哈希码吗?我们可以使用任何随机数吗?
1赞 rajeev pani.. 1/29/2019
@JavaYouth 是的, 你可以
3赞 Darwin 12/29/2021
@JavaYouth ,正如 Rajeev 所提到的,您可以使用任何其他值。但是,建议使用质数,因为这将产生较少的碰撞。
8赞 user104309 12/9/2014 #15

添加到@Lombo的答案

什么时候需要覆盖 equals() ?

Object 的 equals() 的默认实现是

public boolean equals(Object obj) {
        return (this == obj);
}

这意味着只有当两个对象具有相同的内存地址时,它们才会被视为相等,而只有当您具有相同的内存地址时,它们才会被认为是相等的。 将对象与自身进行比较。

但是,如果两个对象对一个对象具有相同的值,则可能需要将两个对象视为相同 或它们的更多属性(请参阅@Lombo的答案中给出的示例)。

因此,在这些情况下,你将覆盖,你会给出你自己的平等条件。equals()

我已经成功实现了equals(),它运行良好。那么,为什么他们也要求覆盖hashCode()呢?

Well.As 只要你不在用户定义的类上使用基于“哈希”的集合,就可以了。 但是在将来的某个时候,您可能想要使用,或者如果您不“正确实现”hashCode(),这些基于哈希的集合将无法按预期工作。HashMapHashSetoverride

仅覆盖等于(对 @Lombo 的答案的补充)

myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

首先,HashMap检查的hashCode是否与。 只有当值相同时,它才会继续检查同一存储桶中的相等性。secondfirst

但是这 2 个对象的 hashCode 是不同的(因为它们具有不同的内存地址 - 与默认实现不同)。 因此,它甚至不在乎检查是否相等。

如果你在被覆盖的 equals() 方法中有一个断点,如果它们有不同的 hashCode,它就不会介入。 检查,只有当它们相同时,它才会调用您的方法。contains()hashCode()equals()

为什么我们不能让 HashMap 检查所有存储桶中的相等性?所以我没有必要覆盖hashCode()!

那么你就错过了基于哈希的集合的意义。 请考虑以下几点:

Your hashCode() implementation : intObject%9.

以下是以存储桶形式存储的密钥。

Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...

比如说,你想知道地图是否包含键 10。 是否要搜索所有存储桶?或者您只想搜索一个存储桶?

根据 hashCode,您可以确定如果存在 10,则它必须存在于 Bucket 1 中。 所以只有 Bucket 1 会被搜索到!

19赞 Chetan 3/4/2015 #16

让我用简单的语言来解释这个概念。

首先,从更广义的角度来看,我们有集合,并且是集合中的数据结构之一。HashMap

要理解为什么我们必须覆盖 和 方法,我们需要首先了解什么是和做什么。equalshashCodeHashMap

A 是一种数据结构,它以数组方式存储数据的键值对。假设 a[],其中 'a' 中的每个元素都是一个键值对。HashMap

此外,上述数组中的每个索引都可以是链表,因此在一个索引处具有多个值。

现在,为什么使用?HashMap

如果我们必须在一个大数组中搜索,那么搜索每个数组都不会有效,所以哈希技术告诉我们,让我们用一些逻辑预处理数组,并根据该逻辑对元素进行分组,即哈希

例如:我们有一个数组 1,2,3,4,5,6,7,8,9,10,11,我们应用哈希函数 mod 10,因此 1,11 将被组合在一起。因此,如果我们必须在前面的数组中搜索 11,那么我们将不得不迭代完整的数组,但是当我们对其进行分组时,我们限制了迭代的范围,从而提高了速度。为简单起见,用于存储上述所有信息的数据结构可以被认为是一个二维数组。

现在,除了上述内容之外,还告诉它不会在其中添加任何重复项。这就是我们必须覆盖 和 的主要原因HashMapequalshashCode

所以,当说到解释内部工作时,我们需要找到它有什么方法,以及它如何遵循我上面解释的上述规则HashMapHashMap

因此,has 方法称为 ,并根据它应遵循上述规则,即有效地分配数组并且不添加任何重复项HashMapput(K,V)HashMap

因此,put 的作用是,它将首先生成给定键,以决定该值应该进入哪个索引。如果该索引上不存在任何内容,则将在那里添加新值,如果那里已经存在某些内容,则应在该索引的链表结束后添加新值。但请记住,不应根据 .假设你有两个 Integer 对象 aa=11,bb=11。hashCodeHashMap

与从对象类派生的每个对象一样,比较两个对象的默认实现是它比较对象内部的引用而不是值。因此,在上述情况下,两者虽然在语义上相等,但都无法通过相等检验,并且存在两个相同值的对象的可能性,从而产生重复。如果我们覆盖,那么我们可以避免添加重复项。 您也可以参考 细节工作hashCode

import java.util.HashMap;


public class Employee {
    String name;
    String mobile;

    public Employee(String name,String mobile) {
        this.name = name;
        this.mobile = mobile;
    }
    
    @Override
    public int hashCode() {
        System.out.println("calling hascode method of Employee");
        String str = this.name;
        int sum = 0;
        for (int i = 0; i < str.length(); i++) {
            sum = sum + str.charAt(i);
        }
        return sum;
    }

    @Override
    public boolean equals(Object obj) {
        // TODO Auto-generated method stub
        System.out.println("calling equals method of Employee");
        Employee emp = (Employee) obj;
        if (this.mobile.equalsIgnoreCase(emp.mobile)) {
            System.out.println("returning true");
            return true;
        } else {
            System.out.println("returning false");
            return false;
        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Employee emp = new Employee("abc", "hhh");
        Employee emp2 = new Employee("abc", "hhh");
        HashMap<Employee, Employee> h = new HashMap<>();
        //for (int i = 0; i < 5; i++) {
            h.put(emp, emp);
            h.put(emp2, emp2);
        //}
        
        System.out.println("----------------");
        System.out.println("size of hashmap: "+h.size());
    }
}

评论

0赞 Vikas Verma 3/13/2017
我有一个困惑,为什么在 HashMap 的情况下重写 hashCode 方法时需要重写 equals 方法?在任何情况下,如果对象的哈希码相等,hashmap 都会替换该值。
0赞 Chetan 3/30/2018
@VikasVerma如果对象的哈希码相等,哈希图不会替换任何类型的值,它只决定必须将新添加到哈希图中的对象放置在哪个索引。现在索引处可以有对象,所以为了避免重复,我们重写了 equals 方法,并编写了逻辑来定义何时将比较中的两个对象视为相等。如果未覆盖,则将存储具有相同值的对象,因为两个对象的引用将不同
4赞 bharanitharan 7/9/2015 #17

考虑将球收集在一个桶中,全部是黑色的。你的工作是按如下方式给这些球上色,并将其用于适当的比赛,

网球 - 黄色,红色。 板球 - 白色

现在水桶有三种颜色的球:黄色、红色和白色。现在你做了着色,只有你知道哪种颜色适合哪个游戏。

给球着色 - 散列。 选择比赛用球 - 等于。

如果你做了着色,有人选择板球或网球的球,他们不会介意颜色!!

5赞 bharanitharan 7/14/2015 #18
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') 将计算哈希值,用于确定 bucket 并使用 method 来查找值是否已经 存在于存储桶中。如果没有,它将被添加,否则它将被替换为当前值hashCode()equals()
  • get('key') 将用于首先查找 Entry(存储桶)并在 Entry 中查找值hashCode()equals()

如果两者都被覆盖,

地图<A>

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

if equals 不被覆盖

地图<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 等价合约

  1. 两个相等的键按照相等的方法应该生成相同的哈希码
  2. 生成相同哈希码的两个键不必相等(在上面的示例中,所有偶数都生成相同的哈希码)
39赞 Premraj 7/16/2015 #19

身份不是平等。

  • 等于操作员测试标识。==
  • 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 生成整数,以便将对象存储在 HashMapHashSet 等数据结构中。

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

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

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

  • 不相等的实例可能具有相同的哈希码。
  • 相等的实例应返回相同的哈希码。
-3赞 Alan Dobbie 2/20/2016 #20

Bah - “你必须在每个覆盖equals()的类中覆盖hashCode()。”

[摘自Joshua Bloch的《Effective Java?

这不是错误的方式吗?重写 hashCode 可能意味着您正在编写一个哈希键类,但重写 equals 肯定不会。有许多类不用作哈希键,但出于其他原因确实需要逻辑相等性测试方法。如果你为它选择“equals”,那么你可能会被要求通过过度应用这条规则来编写一个hashCode实现。所实现的只是在代码库中添加未经测试的代码,这是一个等待将来绊倒某人的邪恶。此外,编写不需要的代码也是反敏捷的。这是错误的(并且 ide 生成的 ide 可能与您手工制作的 equals 不兼容)。

当然,他们应该在写入的对象上强制要求一个接口用作键吗?无论如何,Object 永远不应该提供默认的 hashCode() 和 equals() 恕我直言。它可能鼓励了许多破碎的哈希集合。

但无论如何,我认为“规则”是从后到前写的。与此同时,我将继续避免使用“equals”来表示相等性测试方法:-(

2赞 developer747 3/24/2016 #21

在下面的示例中,如果注释掉 Person 类中等于或哈希码的重写,则此代码将无法查找 Tom 的顺序。使用哈希码的默认实现可能会导致哈希表查找失败。

我下面有一个简化的代码,可以按人拉出人们的顺序。Person 被用作哈希表中的键。

public class Person {
    String name;
    int age;
    String socialSecurityNumber;

    public Person(String name, int age, String socialSecurityNumber) {
        this.name = name;
        this.age = age;
        this.socialSecurityNumber = socialSecurityNumber;
    }

    @Override
    public boolean equals(Object p) {
        //Person is same if social security number is same

        if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
            return true;
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {        //I am using a hashing function in String.java instead of writing my own.
        return socialSecurityNumber.hashCode();
    }
}


public class Order {
    String[]  items;

    public void insertOrder(String[]  items)
    {
        this.items=items;
    }

}



import java.util.Hashtable;

public class Main {

    public static void main(String[] args) {

       Person p1=new Person("Tom",32,"548-56-4412");
        Person p2=new Person("Jerry",60,"456-74-4125");
        Person p3=new Person("Sherry",38,"418-55-1235");

        Order order1=new Order();
        order1.insertOrder(new String[]{"mouse","car charger"});

        Order order2=new Order();
        order2.insertOrder(new String[]{"Multi vitamin"});

        Order order3=new Order();
        order3.insertOrder(new String[]{"handbag", "iPod"});

        Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
        hashtable.put(p1,order1);
        hashtable.put(p2,order2);
        hashtable.put(p3,order3);

       //The line below will fail if Person class does not override hashCode()
       Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
        for(String item:tomOrder.items)
        {
            System.out.println(item);
        }
    }
}
3赞 Aftab 8/28/2016 #22

Java 中的 Equals 和 Hashcode 方法

它们是 java.lang.Object 类的方法,它是所有类(自定义类以及 java API 中定义的其他类)的超类。

实现:

public boolean equals(对象对象 obj)

公共 int hashCode()

enter image description here

public boolean equals(对象对象 obj)

此方法仅检查两个对象引用 x 和 y 是否引用同一对象。即它检查 x 是否 == y。

它是自反的:对于任何参考值 x,x.equals(x) 都应该返回 true。

它是对称的:对于任何参考值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。

它是可传递的:对于任何引用值 x、y 和 z,如果 x.equals(y) 返回 true,y.equals(z) 返回 true,则 x.equals(z) 应返回 true。

它是一致的:对于任何引用值 x 和 y,x.equals(y) 的多次调用始终返回 true 或始终返回 false,前提是没有修改对象的等值比较中使用的信息。

对于任何非 null 引用值 x,x.equals(null) 应返回 假。

公共 int hashCode()

此方法返回调用此方法的对象的哈希代码值。此方法以整数形式返回哈希代码值,并且支持基于哈希的集合类(如 Hashtable、HashMap、HashSet 等)。必须在重写 equals 方法的每个类中重写此方法。

hashCode的一般合约为:

每当在执行 Java 应用程序期间对同一对象多次调用它时,hashCode 方法必须始终如一地返回相同的整数,前提是没有修改对象的等值比较中使用的信息。

此整数不需要在应用程序的一次执行与同一应用程序的另一次执行之间保持一致。

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

根据 equals(java.lang.Object) 方法,如果两个对象不相等,则对两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

相等的对象必须生成相同的哈希代码,只要它们是 相等,但不相等的对象不需要生成不同的哈希代码。

资源:

爪哇牧场

图片

评论

0赞 UdayKiran Pulipati 9/13/2019
图片(视频链接)处于私人模式。公开观看。
1赞 Cleonjoys 6/5/2017 #23

恕我直言,按照规则说 - 如果两个对象相等,那么它们应该具有相同的哈希值,即相等的对象应该产生相等的哈希值。

如上所述,Object 中的默认 equals() 为 ==,它对地址进行比较,hashCode() 以整数(实际地址上的哈希值)返回地址,这对于不同的 Object 也是不同的。

如果你需要在基于 Hash 的集合中使用自定义对象,你需要覆盖 equals() 和 hashCode(),例如如果我想维护 Employee 对象的 HashSet,如果我不使用更强的 hashCode 和 equals,我最终可能会覆盖两个不同的 Employee 对象,当我使用 age 作为 hashCode() 时,就会发生这种情况, 但是,我应该使用唯一值,该值可以是员工 ID。

5赞 ngg 8/28/2017 #24

1)常见错误如下例所示。

public class Car {

    private String color;

    public Car(String color) {
        this.color = color;
    }

    public boolean equals(Object obj) {
        if(obj==null) return false;
        if (!(obj instanceof Car))
            return false;   
        if (obj == this)
            return true;
        return this.color.equals(((Car) obj).color);
    }

    public static void main(String[] args) {
        Car a1 = new Car("green");
        Car a2 = new Car("red");

        //hashMap stores Car type and its quantity
        HashMap<Car, Integer> m = new HashMap<Car, Integer>();
        m.put(a1, 10);
        m.put(a2, 20);
        System.out.println(m.get(new Car("green")));
    }
}

未找到绿色汽车

2. hashCode() 引起的问题

该问题是由 un-overridden 方法引起的。和之间的合同是:hashCode()equals()hashCode()

  1. 如果两个对象相等,则它们必须具有相同的哈希码。
  2. 如果两个对象具有相同的哈希代码,则它们可能相等,也可能不相等。

    public int hashCode(){  
      return this.color.hashCode(); 
    }
    
2赞 Arun Raaj 9/2/2017 #25

String 类和包装类的 和 方法与 Object 类的实现不同。 Object类的equals()方法比较对象的引用,而不是内容。Object 类的 hashCode() 方法为每个对象返回不同的哈希码,无论内容是否相同。equals()hashCode()

当您使用 Map 集合并且键为 Persistent 类型、StringBuffer/builder 类型时,它会导致问题。由于它们不会覆盖 equals() 和 hashCode() 与 String 类不同,因此当您比较两个不同的对象时,equals() 将返回 false,即使它们具有相同的内容。这将使hashMap存储相同的内容密钥。存储相同的内容密钥意味着它违反了 Map 的规则,因为 Map 根本不允许重复的密钥。 因此,您可以覆盖类中的 equals() 和 hashCode() 方法并提供实现(IDE 可以生成这些方法),以便它们的工作方式与 String 的 equals() 和 hashCode() 相同,并防止相同的内容键。

您必须将 hashCode() 方法与 equals() 一起覆盖,因为 equals() 根据哈希码工作。

此外,将 hashCode() 方法与 equals() 一起覆盖有助于完整 equals()-hashCode() 合约:“如果两个对象相等,那么它们必须具有相同的哈希代码。

什么时候需要为 hashCode() 编写自定义实现?

众所周知,HashMap的内部工作是建立在Hashing原则上的。在某些存储桶中存储了条目集。您可以根据需要自定义 hashCode() 实现,以便可以将同一类别的对象存储到同一索引中。 当您使用 Method 将值存储到 Map 集合中时,put() 的内部实现为:put(k,v)

put(k, v){
hash(k);
index=hash & (n-1);
}

这意味着,它生成索引,并且索引是根据特定键对象的哈希码生成的。因此,请使用此方法根据您的要求生成哈希码,因为相同的哈希码条目集将存储到相同的存储桶或索引中。

就是这样!

1赞 Tavash 4/19/2018 #26

为了帮助您检查重复的对象,我们需要一个自定义的 equals 和 hashCode。

由于哈希码总是返回一个数字,因此使用数字而不是字母键检索对象总是很快。它将如何做?假设我们通过传递一些在其他对象中已经可用的值来创建一个新对象。现在,新对象将返回与另一个对象相同的哈希值,因为传递的值是相同的。一旦返回相同的哈希值,JVM 每次都会转到相同的内存地址,如果同一哈希值存在多个对象,它将使用 equals() 方法来识别正确的对象。

1赞 Ammy 7/16/2018 #27

当您想在 Map 中将自定义对象作为键进行存储和检索时,应始终覆盖自定义对象中的 equals 和 hashCode 。 例如:

Person p1 = new Person("A",23);
Person p2 = new Person("A",23);
HashMap map = new HashMap();
map.put(p1,"value 1");
map.put(p2,"value 2");

这里 p1 和 p2 将被视为只有一个对象,大小将仅为 1,因为它们相等。map

1赞 Manash Ranjan Dakua 7/17/2018 #28
public class Employee {

    private int empId;
    private String empName;

    public Employee(int empId, String empName) {
        super();
        this.empId = empId;
        this.empName = empName;
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    @Override
    public String toString() {
        return "Employee [empId=" + empId + ", empName=" + empName + "]";
    }

    @Override
    public int hashCode() {
        return empId + empName.hashCode();
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }
        if (!(this instanceof Employee)) {
            return false;
        }
        Employee emp = (Employee) obj;
        return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName());
    }

}

测试类

public class Test {

    public static void main(String[] args) {
        Employee emp1 = new Employee(101,"Manash");
        Employee emp2 = new Employee(101,"Manash");
        Employee emp3 = new Employee(103,"Ranjan");
        System.out.println(emp1.hashCode());
        System.out.println(emp2.hashCode());
        System.out.println(emp1.equals(emp2));
        System.out.println(emp1.equals(emp3));
    }

}

在对象类中,equals(Object obj) 用于比较地址比较,这就是为什么在 Test 类中,如果您比较两个对象,则等于给出 false 的方法,但是当我们覆盖 hashcode() 时,它可以比较内容并给出正确的结果。

评论

0赞 Manash Ranjan Dakua 7/17/2018
和我在下面的程序中添加的测试类。
0赞 Manash Ranjan Dakua 7/17/2018
在对象类中,equals(Object obj) 用于比较地址比较,这就是为什么在 Test 类中,如果您比较两个对象,则等于给出 false 的方法,但是当我们覆盖 hashcode() 时,它可以比较内容并给出正确的结果。
1赞 Suraj Rao 7/17/2018
您可以使用此答案下方的编辑链接添加到您的答案中。请不要将一个答案添加为两个不完整的答案
3赞 91StarSky 3/12/2020 #29

如果覆盖而不是 ,则不会发现任何问题,除非您或其他人在哈希集合中使用该类类型,例如 。 在我之前的人已经多次清楚地解释了记录在案的理论,我在这里只是提供一个非常简单的例子。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()RishavhashCode

    true
    -----------------------------------
    false
    -----------------------------------
    2
0赞 steve 11/6/2020 #30

这个答案中没有提到测试equals/hashcode合约。

我发现 EqualsVerifier 库非常有用且全面。它也非常易于使用。

此外,从头开始构建和方法涉及大量样板代码。Apache Commons Lang 库提供了 EqualsBuilderHashCodeBuilder 类。这些类大大简化了复杂类的实现和方法。equals()hashCode()equals()hashCode()

顺便说一句,值得考虑重写该方法以帮助调试。Apache Commons Lang 库提供了 ToStringBuilder 类来帮助解决这个问题。toString()

51赞 Panagiotis Bougioukos 3/29/2021 #31

为什么我们覆盖 equals() 方法

在 Java 中,我们不能重载 ==、+=、-+ 等运算符的行为方式。他们以某种方式行事。因此,让我们在这里关注运算符 ==。

运算符 == 的工作原理。

它检查我们比较的 2 个引用是否指向内存中的同一实例。仅当这 2 个引用表示内存中的同一实例时,运算符才会解析为 true。==

所以现在让我们考虑下面的例子

public class Person {

      private Integer age;
      private String name;
    
      ..getters, setters, constructors
      }

因此,假设在您的程序中,您在不同的地方构建了 2 个 Person 对象,并且您希望比较它们。

Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 );  --> will print false!

从业务角度来看,这两个对象看起来是一样的,对吧?对于 JVM,它们是不一样的。由于它们都是使用关键字创建的,因此这些实例位于内存中的不同段中。因此,运算符 == 将返回 falsenew

但是,如果我们不能覆盖 == 运算符,我们怎么能对 JVM 说我们希望将这两个对象视为相同。有方法在起作用。.equals()

您可以覆盖以检查某些对象是否具有相同的值,以便将特定字段视为相等。equals()

您可以选择要比较的字段。如果我们说 2 个 Person 对象在且仅当它们具有相同的年龄和相同的名称时才相同,那么 IDE 将创建如下所示的内容来自动生成 equals()

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }

让我们回到前面的例子

    Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1 == person2 );   --> will print false!
    System.out.println ( person1.equals(person2) );  --> will print true!

因此,我们不能重载 == 运算符来以我们想要的方式比较对象,但 Java 给了我们另一种方法,即方法,我们可以根据需要覆盖它。equals()

但是请记住,如果我们没有在类中提供自定义版本(又名覆盖),那么预定义的 Object 类和运算符的行为将完全相同。.equals().equals()==

继承自 Object 的默认方法将检查两个比较的实例在内存中是否相同!equals()

为什么我们覆盖 hashCode() 方法

java 中的一些数据结构,如 HashSet、HashMap 根据应用于这些元素的哈希函数存储它们的元素。哈希函数是hashCode()

如果我们可以选择覆盖方法,那么我们也必须选择覆盖方法。这是有原因的。.equals()hashCode()

其默认实现继承自 Object 认为内存中的所有对象都是唯一的!hashCode()

让我们回到那些哈希数据结构。这些数据结构有一个规则。

HashSet 不能包含重复值,HashMap 不能包含重复键

HashSet 是在后台使用 HashMap 实现的,其中 HashSet 的每个值都作为键存储在 HashMap 中。

因此,我们必须了解 HashMap 的工作原理。

简单来说,HashMap 是一个具有一些存储桶的原生数组。每个存储桶都有一个 linkedList。在该 linkedList 中,我们的密钥被存储。HashMap 通过应用方法为每个键找到正确的 linkedList,然后遍历该 linkedList 的所有元素,并对每个元素应用方法以检查该元素是否已包含在那里。不允许使用重复的密钥。hashCode()equals()

enter image description here

当我们将某些内容放入 HashMap 中时,密钥将存储在其中一个 linkedList 中。该键将存储在哪个 linkedList 中,由该键上的方法结果显示。因此,如果结果为 4,则该 key1 将存储在数组的第 4 个存储桶中,位于该数组存在的 linkedList 中。hashCode()key1.hashCode()

默认情况下,方法为每个不同的实例返回不同的结果。如果我们有默认的,其行为类似于 ==,它将内存中的所有实例视为不同的对象,那么我们没有任何问题。hashCode()equals()

但在前面的示例中,我们说过,如果 Person 实例的年龄和名称匹配,我们希望它们被认为是相等的。

    Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!

现在,让我们创建一个映射,将这些实例存储为键,并将一些字符串作为对值

Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");

在 Person 类中,我们没有重写方法,但我们已经重写了方法。由于默认值为不同的 java 实例提供不同的结果,并且有很大的机会获得不同的结果。hashCodeequalshashCodeperson1.hashCode()person2.hashCode()

我们的地图可能以不同 linkedList 中的那些人结束。

enter image description here

这违背了 HashMap 的逻辑

一个 HashMap 不允许有多个相等的键!

但是我们现在有,原因是从对象类继承的默认值是不够的。在我们重写 Person 类的方法之后,情况就不行了。hashCode()equals()

这就是为什么我们必须在重写方法后重写方法的原因。hashCode()equals

现在让我们解决这个问题。让我们重写我们的方法,以考虑考虑的相同字段,即hashCode()equals()age, name

 public class Person {

      private Integer age;
      private String name;
    
      ..getters, setters, constructors

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

      }

现在,让我们再次尝试将这些键保存在我们的 HashMap 中

Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");

person1.hashCode()而且肯定会是一样的。假设它是 0。person2.hashCode()

HashMap 将转到存储桶 0,其中 LinkedList 会将 person1 保存为值为“1”的键。对于第二个放置,HashMap 足够智能,当它再次进入存储桶 0 以保存值为“2”的 person2 键时,它会看到那里已经存在另一个相等的键。因此,它将覆盖上一个密钥。因此,最终只有 person2 键将存在于我们的 HashMap 中。

enter image description here

现在我们符合 Hash Map 的规则,即不允许使用多个相等的键!

评论

0赞 Nexen 2/19/2022
这里关注的点是 .您使用的字段应始终为 .否则,对象的哈希码可能会被更改,这将导致很多问题和其他问题。请参阅 docs.oracle.com/javase/6/docs/api/java/lang/.....getters, setters, constructorsObjects.hash(name, age)finalSet/Map