hashmap如何确定正确的密钥而不调用equals()

发布于 2025-02-09 18:24:32 字数 1968 浏览 2 评论 0原文

这是我将用作密钥的person类的示例。

它覆盖equals(),以便它始终返回falsehashcode()始终返回1

public class Person {
    String name;
    int age;


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

    @Override
    public boolean equals(Object o) {
        System.out.println("Equals is called");
        return false;
    }

    @Override
    public int hashCode() {
        System.out.println("Hashcode is called");
        return 1;
    }
}

现在,让我们将其用作钥匙,然后随机填充地图。

public class Main {

    public static void main(String[] args) {

        var person1 = new Person("Mike", 21);
        var person2 = new Person("Alex", 32);
        var person3 = new Person("Andrew", 45);

        Map<Person, String> peopleMap = new HashMap<>();

        peopleMap.put(person3, "SomeValue3");
        peopleMap.put(person2, "SomeValue1");
        peopleMap.put(person1, "SomeValue2");

        System.out.println("Getting a person \n \n");
        System.out.println(peopleMap.get(person3));
    }
}

这是我在控制台中看到的内容:

Hashcode is called
Hashcode is called
Equals is called
Hashcode is called
Equals is called
Equals is called

Getting a person 

Hashcode is called
SomeValue3

问题:

  1. 如果等于始终是false

    ,它如何确定正确的密钥?

  2. 如何在不调用equals()的情况下获得正确的密钥?

    获取对象?

  3. 如果我们移动person3对象(我们要得到的一个对象),以便在中间 - 1 equals()将用于第一个对象。如果我们移动到3个位置,则是这样:

      peoplemap.put(person2,“ somevalue1”);
     peoplemap.put(person1,“ somevalue2”);
     peoplemap.put(person3,“ somevalue3”);
     

2 times equals()将被调用(用于person2person1 /code>),但没有我们获得的密钥。

为什么是这样?我一直认为地图不能存储订单,但看起来像这样。

Here is an example of a Person class that I will use as a key.

It overrides equals() so that it always returns false and hashCode() that always returns 1.

public class Person {
    String name;
    int age;


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

    @Override
    public boolean equals(Object o) {
        System.out.println("Equals is called");
        return false;
    }

    @Override
    public int hashCode() {
        System.out.println("Hashcode is called");
        return 1;
    }
}

Now let's use it as a key and randomly fill the map.

public class Main {

    public static void main(String[] args) {

        var person1 = new Person("Mike", 21);
        var person2 = new Person("Alex", 32);
        var person3 = new Person("Andrew", 45);

        Map<Person, String> peopleMap = new HashMap<>();

        peopleMap.put(person3, "SomeValue3");
        peopleMap.put(person2, "SomeValue1");
        peopleMap.put(person1, "SomeValue2");

        System.out.println("Getting a person \n \n");
        System.out.println(peopleMap.get(person3));
    }
}

Here is what I see in a console:

Hashcode is called
Hashcode is called
Equals is called
Hashcode is called
Equals is called
Equals is called

Getting a person 

Hashcode is called
SomeValue3

Questions:

  1. How could it determine the right key if the equals is always false?

  2. How could it get the right key without even calling equals() for getting an object by the key?

  3. If we move adding the person3 object down ( the one we are getting ) so it is in the middle - 1 equals() will be called for the first object. If we move adding to the 3 position, like this:

     peopleMap.put(person2, "SomeValue1");
     peopleMap.put(person1, "SomeValue2");
     peopleMap.put(person3, "SomeValue3");
    

2 times equals() will be called ( for person2 and person1 ) but none for the key we are getting.

Why is it so? I always thought that map can't store an order, but look like this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

懒的傷心 2025-02-16 18:24:32

您的等于实施违反了该方法的合同,特别是Javadoc中的第一个条款:

它是反身的:对于任何非编号参考值x,x. equars(x)都应返回true。

但是,hashmap实现假定条件所持,并且作为优化,将首先检查密钥对象是否通过参考为是否相同,然后再使用equals。当您查看hashmap的源代码时,您可以看到这一点。

https://github.com/openjdk/jdk/blob/0f801fe6fd2fcc181121f9846f9846f6869ca3a03e18a/src/src/java.base/java.base/share/share/share/share/classes/classes/java/java/java/java/java/java/hashmap.java-java-java-jjava-jjava-jjava#l57-pha

    final Node<K,V> getNode(Object key) {
...
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
...

youtha ihava i>通过调试器执行。

Your equals implementation violates the contract of the method, specifically the first clause in the javadoc:

It is reflexive: for any non-null reference value x, x.equals(x) should return true.

However the HashMap implementation assumes the condition holds and as an optimization will first check if the key object is the same by reference before using equals. You can see this when you look at the source code of HashMap.

https://github.com/openjdk/jdk/blob/0f801fe6fd2fcc181121f9846f6869ca3a03e18a/src/java.base/share/classes/java/util/HashMap.java#L577-L579

    final Node<K,V> getNode(Object key) {
...
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
...

You can also confirm this by stepping through the execution with a debugger.

纸伞微斜 2025-02-16 18:24:32

hashmap在引擎盖下维护 buckets array 。每个桶对应于一系列哈希。 buckets 中的条目表示为 nodes ,形式链接列表 red> red-> red-black tree (取决于 bucket 中的节点)。

您已经创建了一个具有损坏的hashcode()的类,该类别始终会产生相同的哈希,而当您将其用作键时,它们最终将进入 相同的桶 。因此,这些对象基本上会导致 碰撞 hashmap中。

在使用person的实例为 和调用get()提供一个提供其中一个之一现有键,此方法将在内部调用getNode()

getNode()会找到相应的 bucket (基于提供的 key hash ),如果是此存储桶的内容不是null - IE包含A list 或A tree ,它将穿越 list tree )检查每个 node ,然后首先在调用equals())之前,在这种情况下 em>)它将使用键上的身份比较

(k = e.key) == key

如果指向同一对象,则此比较很快,无需使用equals())进行比较()复杂对象图)。

这就是为什么即使损坏的等于/hashcode hashmap也能够识别所需的条目,当您给出与的完全相同的对象时。

HashMap maintains an array of buckets under the hood. Each bucket corresponds to a range of hashes. Entries in the buckets are represented as nodes which form either a Linked list or Red-black tree (depending on the number of nodes in the bucket).

You've created a class with a broken hashCode() which instances always produce identical hashes and when you use them as keys they will end up in the same bucket. So basically these objects would cause collisions in the HashMap.

After you've populated the HashMap using instances of Person as keys and invoking get() providing one of the existing keys, this method will internally invoke getNode().

getNode() would find the corresponding bucket (based on the hash of the provided key), and if the contents of this bucket isn't null - i.e. the bucket contains either a list or a tree, it would traverse through the list (tree) checking each node and firstly before invoking equals() (which is useless in this case) it would use the identity comparison on keys.

(k = e.key) == key

And if the keys point to the same object, this comparison is fast and there's no need to resort to comparison using equals() (which might be costful in case of the complex object graph).

That's why even with broken equals/hashCode HashMap is able to identify the required entry, when you're giving exactly the same object as a key.

七颜 2025-02-16 18:24:32
  1. 如果equals始终为false,它如何确定正确的键?

  2. 如何获得正确的键,甚至不调用按键获取对象的平等?

您可以参考hashtable内部工作以获取详细信息,因为很难在此处解释。简而言之。

  • 每个基于哈希的数据架构内部都遵循hashtable
    Map<Person, String> peopleMap = new HashMap<>();
  • 这里init_capacity = 16(hashmap)=&gt; 16不同的 bucketid's [0-15],就像一系列参考一样,最初值= null

每个阵列的每个插槽称为 bin/bucket

  • 内部喜欢,bucketid = @overrided hashcode +一些内部计算

  • 您的hashcode()每次都返回 1 Inge> code> bucketid = 1 +(Say)3 = 4 [每次] 。

     peopleMap.put(person3, "SomeValue3");
  1. 调用 hashcode一次 =&gt; bucketid = 4是空的,因此JVM不调用 equals()&amp;直接附加数据(节点)(简单语言)。
     peopleMap.put(person2, "SomeValue1");
  1. 调用 hashcode一次 =&gt; bucketid = 4。现在调用1个时间,因为检查person2值是否等于先前的添加值IE。人3。
     peopleMap.put(person1, "SomeValue2");

3. INVOKES hashcode一次 =&gt; bucketid = 4。现在调用2次检查重复。 IE。 person3的1次,person2的第二名。

因此输出:

hashcode称为

hashcode称为

等于

hashcode称为

等于

等于

”图:

希望这会有所帮助:)

  1. How could it determine the right key if the equals is always false?

  2. How could it get the right key without even calling an equals for getting an object by the key?

you can refer hashTable internal working for detail information as it is difficult to explain it here. Have tried to explain in short.

  • Every hash-based DataStructure internally follows HashTable.
    Map<Person, String> peopleMap = new HashMap<>();
  • here init_capacity=16 (HashMap) => 16 distinct bucketId's [0-15] , like an array of references, initially value= null.

each slot of array is called as bin/bucket

Java HashTable internally use Singly Linear LinkedList

  • bucketId calculated internally like, bucketId = @Overrided HashCode + some internal calculation

  • your Hashcode() returns everytime 1 implies bucketId = 1 + (say)3 = 4 [everytime].

     peopleMap.put(person3, "SomeValue3");
  1. invokes Hashcode one time => bucketId = 4 is empty initially so JVM do NOT invokes equals() & directly attach data (node)(in simple language).
     peopleMap.put(person2, "SomeValue1");
  1. invokes Hashcode one time => bucketId = 4. Now invokes equals 1 time since to check if person2 value equal to previous added value ie. person3.
     peopleMap.put(person1, "SomeValue2");

3.invokes Hashcode one time => bucketId = 4. Now invokes equals 2 times to check duplicates . ie. 1time for person3, 2ndly for person2.

hence output:

Hashcode is called

Hashcode is called

Equals is called

Hashcode is called

Equals is called

Equals is called

DIAGRAM:

HOPE THIS HELPS :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文