Question

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?

Was it helpful?

Solution

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:

  • Use a 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().
  • If you use Guava, you can create one or more Equivalences 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.

OTHER TIPS

You can use TreeSet<Person> with your own comparator

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top