Reputation: 205
I know I should have made the Public Class immutable but still I already changed the name of the a Person object before it was added to the Set set of type HashSet
and my Person class also implements the hashCode()
and equals() methods which help to check weather an object is same as the previous object or not and prevent addition to the list if it is equivalent with the help of the hashCode()
and the equals() methods, but still the output is :
bob
charlie
bob
instead of: bob charlie
If i replace the code piece of the Main Class:
Set<Person> set = new HashSet<Person>();
Person a= new Person("alice",45);
Person b=new Person("bob",41);
Person c= new Person("charlie",48);
set.add(a);
a.name="bob";
set.add(b);
set.add(c);
By the following code down below (i.e. I declared an equivalent object directly without changing the name later as in the original problematic code):
Set<Person> set = new HashSet<Person>();
Person a= new Person("bob",45);
Person b=new Person("bob",41);
Person c= new Person("charlie",48);
set.add(a);
set.add(b);
set.add(c);
Then the object b is not added.
And also another point to note is that I changed the name of object(in the original problematic code) a before the addition of object b but still it is adding b why????
My original problematic code which contains the Main Class followed by the Public Class is given in full detail below::
//Main.java class of generics_practice_test package;
package generics_practice_test;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
public class Main {
public static void main (String args[])
{
Set<Person> set = new HashSet<Person>();
Person a= new Person("alice",45);
Person b=new Person("bob",41);
Person c= new Person("charlie",48);
set.add(a);
a.name="bob";
set.add(b);
set.add(c);
for(Iterator<Person> iterator=set.iterator();iterator.hasNext();){
System.out.println(iterator.next());
}
}
}
And the code for the Person Class is below: //Person class of generics_practice_test package is below
package generics_practice_test;
public class Person implements Comparable<Person> {
public String name;
public int age;
public Person(String name,int age)
{
this.name=name;
this.age=age;
}
public String toString()
{
return this.name;
}
public int compareTo(Person o)
{
int myLength=name.length();
int oLength=o.name.length();
if(myLength == oLength) return 0;
if(oLength > myLength) return -1;
return 1;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Upvotes: 0
Views: 2005
Reputation: 4504
HashSet
internally uses HashMap
and HashMap is an implementation of hashtable
data-structure.
Hashtables
uses has a concept of storing data in buckets.
Think of it like a map, where key is the hashCode
and the value a list of elements.
Now what is happening here is that at the time of storing, it gets the hashCode()
and stores the current element wherever it fit possible.
In you case it is name
hashCode. Now takes for an example your bob's hash value is 32 so there will be an entry with 32 as key and list will contain the element.
You change the name which does not really change the position of the element.
While inserting HashSet follows the below steps.
Upvotes: 0
Reputation: 85779
A HashSet
will store the elements based on the result of hashCode
method. This is a pseudo algorithm used in the implementation:
hashCode
. Store it in int possibleIndex
.possibleIndex
. Store it in index
.List
stored in index
. Traverse this list and seek if the object is not inside this list by using equals
method. This is do in order to handle collisions, or objects with the same hashCode
.Note that for your example, Person#hashCode
implementation relies on the name
element only.
Let's review your first piece of code:
//create the new HashSet
Set<Person> set = new HashSet<Person>();
//create your elements to be inserted
Person a= new Person("alice",45);
Person b= new Person("bob",41);
Person c= new Person("charlie",48);
//try to add "a". It will calculate hashCode from name field,
//which value is "alice"
set.add(a);
//you change the name here, but it won't affect the previous
//operation because the index for "a" was calculated using "alice",
//not "bob" and IT WONT BE RECALCULATED!
a.name="bob";
//try to add "b". It will calculate hashCode from name field,
//which value is "bob"
//as noted before, there's no index based on "bob"'s hashCode,
//so it will be added with no problems
set.add(b);
//try to add "c". It will calculate hashCode from name field,
//which value is "charlie"
set.add(c);
So, all the elements are inserted with no problems because there's no collision of hashCode
(that's why previous explanation doesn't cover the call to equals
method).
Now, let's review your second piece of code:
//create the new HashSet
Set<Person> set = new HashSet<Person>();
//create your elements to be inserted
Person a= new Person("bob",45);
Person b=new Person("bob",41);
Person c= new Person("charlie",48);
//try to add "a". It will calculate hashCode from name field,
//which value is "bob"
set.add(a);
//try to add "b". It will calculate hashCode from name field,
//which value is "bob"
//since the index calculated from the hashCode of "bob" is
//already inserted, it will check if the element already exists
//Looking at Person#equals, which is based on name field only
//there is an element where the name field has a value of "bob"
//"b" won't be inserted
set.add(b);
//try to add "c". It will calculate hashCode from name field,
//which value is "charlie"
set.add(c);
Upvotes: 3
Reputation: 13947
try changing your equals method to this
@Override
public boolean equals(Object obj) {
if (obj == null) //first you check if object is null
return false;
if (getClass() != obj.getClass()) // then you check if object is of the same class
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
the statement if (this == obj)
checks the objects by address in memory. Address in memory of every object will be different! if you used new
on the object
Upvotes: -1