在JAVA中更改HashSet中对象的属性值的问题[重复]

slsn1g29  于 5个月前  发布在  Java
关注(0)|答案(3)|浏览(55)

此问题在此处已有答案

Java HashSet contains duplicates if contained element is modified(7个答案)
6天前关闭
我正在学习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”来获得所有元素的唯一性,对吗?提前谢谢你。

dsekswqp

dsekswqp1#

HashSet使用“equals”来获得所有元素的唯一性,对吗?
不是
HashSet是HashMap的一个facade,其中的项存储在键集中。HashMap创建并比较来自其内部哈希节点的键hashCode。
一旦项目存储在其hashCode数组元素下,它就在那里。更改hashCode不会替换它。你写的是一个不好的例子如何在HashMap中使用hashCode。
contains方法将检查底层hashmap键集的键(哈希数组),而不是调用所有存储对象的equals方法。

bfhwhh0e

bfhwhh0e2#

这是因为通过修改Node的值,你也改变了hashcode(由于你的hashcode覆盖)。这通常不是这种情况,也不应该是这种情况,因为你不希望hashcode是可变的。它应该是常量和不变的。
HashSet的底层代码使用HashMap,contains()使用的内部方法将使用对象的hashcode来确定存储节点的bucket。因此,当您的hashcode设置为值1时,您调用add(),节点n1存储在bucket 1中。当您将值更改为5时,hashcode现在是5,调用s.contains(n1)将尝试检查bucket 5中的节点是否为空(null)。
正确的方法是删除你实现的hashcode覆盖,让默认实现完成它的工作。另请参阅:

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;
    }
}

字符串
输出量:
没有哈希码覆盖

Node@4783da3f
YES
5
5
Node@4783da3f
Still here


使用Hashcode override

Node@1
YES
5
5
Node@5

fxnxkyjh

fxnxkyjh3#

你需要理解按引用传递和按值传递的区别。你对hashcode的重写实际上是有效的。但是,你没有创建一个新对象,n1和s中的对象实际上是同一个对象。

相关问题