Reputation: 4647
Situation:
Let's assume that I have a class like this:
class Person {
public String name;
public String surname;
public age;
}
Now I want to create a set which contains some People on the basis of some prevoiusly created list (containing some duplicates). Of course, when I create set I don't want to have any duplicates inside.
HashSet<Person> mySet = new LinkedHashSet<Person>(listOfPeople);
Problem: Let's assume, that in some cases, "no duplicates" means for me "people with different names". In other case "people with different age" etc.
I see that HashSet
's add
method uses a put
from HashMap
:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // <== !!
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
Question:
So, I understand that I should simply override an equals
in Person
and remember to return the same hashCode
for objects which should be "equal" according to my requirements. But what if my "equality" term changes in runtime?
I'm asking, because while sorting a collection using Collections.sort
I can specify a custom Comparator
which enables me to provide a comparing logic depending on the situation.
Is there any analogical mechanism, or solution you know, which enables me to decide in runtime, if components are equal or not while creating a set of elements?
The only solution I have now is to define some static Comparator in Person class and then, override an equals method in that way that it uses this comparator. Then, by replacing a Comparator in Person I'm actually changing the equals logic... Does it make sense?
Upvotes: 0
Views: 82
Reputation: 111
you have to modify a bit in your Person class and use this customized hashSet
public class CustomHashSet extends HashSet {
@Override
public boolean add(Object e) {
boolean flag = true;
Iterator<Object> itr = this.iterator();
while(itr.hasNext()){
if(itr.next().hashCode()==(e.hashCode())){
flag = false;
break;
}
}
if(flag)
flag = flag & super.add(e);
return flag;
}
}
Modification in the person class
public class Person {
public String name;
public String surname;
public int age;
@Override
public boolean equals(Object obj) {
return (this == obj);
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + age;
return result;
}
}
Use your own logic int the hashcode method duplication checking.
testing
public class TestB {
public static void main(String[] args) {
HashSet a = new CustomHashSet();
Person p = new Person("test","test1",1);
Person p1 = new Person("test","test1",1);
a.add(p);
a.add(p1);
System.out.println(a);
}
} this will add only one element duplicate element not allowed. hope this will helpful for you. U can also use comparable interface implementation also.
Upvotes: 0
Reputation: 121780
The only solution I have now is to define some static Comparator in Person class and then, override an equals method in that way that it uses this comparator
Don't do that. Nothing in the Comparator
contract requires that two objects which are different with regards to .equals()
compare to non zero. The only constraint which can be placed is that if two objects are .equals()
then compared to one another they give 0. The implementation of Comparable
is then said to be "compatible with equals". But some classes don't even respect that in the JDK (see BigDecimal
).
You have two choices:
SortedSet
. A TreeSet
for instance allows you to pass a Comparator
at runtime. A SortedSet
evaluates equivalence to the fact that two instances compared to one another give 0, regardless of .equals()
.Equivalence
s for your objects; your Set
will then have to have members of type Equivalence.Wrapper<Person>
instead of just Person
and you'll have to .add(eq.wrap(person))
, but it works.Upvotes: 1