更改 JAVA 中 HashSet 内对象的属性值时出现问题

问题描述 投票:0回答:2

我正在学习java并面临一个非常奇怪的问题,我认为用我的代码来解释这个更容易

这就是我的课:

class Node
{
    private int val;

    public Node(int val)
    {
        this.val = val;
    }

    public int getVal()
    {
        return this.val;
    }

    public void setVal(int newVal)
    {
        this.val = newVal;
    }

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

    @Override
    public boolean equals(Object obj) {
        System.out.println("Inside equal of " + this.val);  
       //i expect it will print out when i use set.contains()
        if(this.hashCode() == ((Node)obj).hashCode())
        {
            return true;
        }
        return false;
    }
}

这是我使用 HashSet 与 Node 类一起使用的主块

public class Main
{
    public static void main(String[] args)
    {
        Set<Node> s = new HashSet<>();
        Node n1 = new Node(1);
        s.add(n1);
        /*as i know, when we add a object to the set, we actually add a refference to the origin                             memory area (the one that n1 points to) to the set*/
        
        if(s.contains(n1))
        {
            System.out.println("YES");
        }
        
        for(Node i : s)
        {
            i.setVal(5);  //i expect n1 to change too and it does
        }

        for(Node i : s)
        {
            System.out.println(i.getVal());
        }
        System.out.println(n1.getVal());   //n1 changes
        
        if(s.contains(n1))
        {
            System.out.println("Still here");
            //This is where i don't understand why this does not print ?
        }
        System.out.println();
    } 
}

这是我的命令

D:\Desktop\draft\JavaDraft>java Main
YES
5
5

我不明白为什么 set 没有意识到 n1 仍在其中,以及为什么 Node 类的函数“equals”内部的代码没有被触发,因为据我所知,HashSet 使用“equals”来获取其所有元素的唯一性正确的?先谢谢你了。

java equals hashset
2个回答
0
投票

这是因为通过修改节点的值,您也正在更改哈希码(感谢您的哈希码覆盖)。 HashSet 的底层代码使用 HashMap,

contains()
使用的内部方法将使用对象的哈希来确定存储您的节点的桶。因此,当您的哈希码设置为值 1 时,它会被存储例如在存储桶 1 中。当您将值更改为 5 时,哈希码现在为 5,并且将尝试检查存储桶 5 中是否为空 (null) 节点。

正确的继续方法是删除已实现的哈希码覆盖并让默认实现完成其工作。另请参阅:

import java.util.*;

public class Main {
    public static void main(String[] args)
    {
        Set<Node> s = new HashSet<>();
        Node n1 = new Node(1);
        System.out.println(n1);
        s.add(n1);
        /*as i know, when we add a object to the set, we actually add a refference to the origin                             memory area (the one that n1 points to) to the set*/

        if(s.contains(n1))
        {
            System.out.println("YES");
        }

        for(Node i : s)
        {
            i.setVal(5);  //i expect n1 to change too and it does
        }

        for(Node i : s)
        {
            System.out.println(i.getVal());
        }
        System.out.println(n1.getVal());   //n1 changes

        System.out.println(n1);
        if(s.contains(n1))
        {
            System.out.println("Still here");
            //This is where i don't understand why this does not print ?
        }
        System.out.println();
    }
}

class Node
{
    private int val;

    public Node(int val)
    {
        this.val = val;
    }

    public int getVal()
    {
        return this.val;
    }

    public void setVal(int newVal)
    {
        this.val = newVal;
    }

    @Override
    public boolean equals(Object obj) {
        System.out.println("Inside equal of " + this.val);
        //i expect it will print out when i use set.contains()
        if(this.hashCode() == ((Node)obj).hashCode())
        {
            return true;
        }
        return false;
    }
}

0
投票

您需要了解按引用传递和按值传递之间的区别。您对哈希码的覆盖实际上生效了。然而,你并没有创建一个新的对象,n1和s中的对象实际上是同一个。

© www.soinside.com 2019 - 2024. All rights reserved.