Reputation: 21
I'm studying java and facing a really weird problem, i think it's easier to explain this along with my code
So this is my class:
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;
}
}
And this is my Main block using HashSet that working with class 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();
}
}
This is my cmd
D:\Desktop\draft\JavaDraft>java Main
YES
5
5
I don't understand why set does not realize n1 still be in it and why the code inside function "equals" of class Node is not triggered because as i know, HashSet uses "equals" to get uniqueness of all of it's element right? Thank you in advance.
Upvotes: 0
Views: 110
Reputation: 1224
That is because by modifying the value of the Node, you are also changing the hashcode (thanks to your hashcode override). This is generally not the case and should not be the case as you do not want the hashcode to be variable. It should be constant and unchanging.
The underlying code of HashSet uses a HashMap, and the internal methods that contains()
uses will use the hashcode of the object to determine a bucket to store your node in. Thus, when your hashcode is set to the value 1, and you call add()
, the node n1 is stored for example at bucket 1. When you changed the value to 5, the hashcode is now 5 and calling s.contains(n1)
will try to check for your node in bucket 5 which is empty (null).
A correct way to proceed will be to delete your implemented hashcode override and let the default implementation do its job. See also:
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;
}
}
Output:
Without Hashcode override
Node@4783da3f
YES
5
5
Node@4783da3f
Still here
With Hashcode override
Node@1
YES
5
5
Node@5
Upvotes: 1
Reputation: 12085
HashSet uses "equals" to get uniqueness of all of it's element right?
Nope
HashSet is an facade for a HashMap, where the items are stored in the key set. The HashMap creates and compares the key hashCode from it's internal hash nodes.
Once the item is stored under its hashCode array element, it is there. Changing the hashCode will not replace it. What you wrote is a bad example how not-to-use the hashCode in the HashMap.
The contains method will check the key (hash array) of the underlying hashmap key set, not calling the equals method of all stored objects.
Upvotes: 1
Reputation: 1
You need to understand the difference between pass-by-reference and pass-by-value. Your override of the hashcode actually takes effect. However, you did not create a new object, the object in n1 and s is actually the same one.
Upvotes: -1