1

I was working with the TreeSet collection with the following code:

import java.util.*;
public class Employee implements Comparable<Employee>{

    private int ID;

    public Employee(int iD) {
        ID = iD;
    }

    @Override
    public int compareTo(Employee obj) {
        return this.ID-obj.ID;
    }

    private static void intoTreeSet() {
        Employee e1=new Employee(4);
        Employee e2=new Employee(2);
        Employee e3=new Employee(1);
        Employee e4=new Employee(5);
        Employee e5=new Employee(3);

        Employee eTemp=new Employee(3);

        Set<Employee> set=new TreeSet();
        set.add(e1);set.add(e2);set.add(e3);set.add(e4);set.add(e5);

        System.out.println("output says: ");
        for(Employee e:set){
            System.out.print(e.ID+" ~ ");
        }
        System.out.println();
        if(set.contains(eTemp)){
            System.out.println("C O N T A I N S !!!");
        }

        if(e5.equals(eTemp)){
            System.out.println("E Q U A L S !!!");
        }
    }

    public static void main(String[] args) {
        intoTreeSet();
    }
}

Output

output says: 
1 ~ 2 ~ 3 ~ 4 ~ 5 ~ 
C O N T A I N S !!!

I am confused to see the output. I want to know, if it does NOT pass equals case, then how come it pass contains case.

I know that two objects can only be equal if their class overrides equals method and they are equal according to some property. I intentionally did not override equals method to see how contains work. If it were a non-tree based collection lets say an ArrayList it would NOT have passed contains test. Why is it so ? can any one explain this behaviour and clear my confusion.

JPG
  • 1,247
  • 5
  • 31
  • 64
  • 3
    "a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal." – Hot Licks Jan 04 '15 at 03:27
  • 1
    A similar question from a couple of years ago: http://stackoverflow.com/questions/12761532/equals-and-comparable-with-sets – FoggyDay Jan 04 '15 at 03:29
  • http://stackoverflow.com/questions/6476600/ – Yousha Aleayoub Sep 04 '16 at 16:53

2 Answers2

6

The javadoc for java.util.TreeSet says:

Note that the ordering maintained by a set (whether or not an explicit comparator is provided) must be consistent with equals if it is to correctly implement the Set interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Set interface is defined in terms of the equals operation, but a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal. The behavior of a set is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Set interface.

In other words, the implementations of compareTo and equals must be consistent with each other. If they're not, the behaviour of TreeSet will be erratic. It may work, it may not. To understand when it does and when it doesn't you need to look closely at the TreeSet implementation, but since the javadoc is very explicit on the conditions under which TreeSet works, it's really not a good idea to try and subvert it.

skaffman
  • 398,947
  • 96
  • 818
  • 769
  • An all-time classic. I have seen at least 50 questions tagged 'treeset' which basically ask the same question and get the same (correct) answer. I wonder why so many people do not search here before asking. ;-) – kriegaex Jan 04 '15 at 12:26
3

The most important point to remember here is, a TreeSet is a SortedSet which performs element comparisons using compareTo (or compare) method.

The Employee class is comparable. From the definition from the docs of the comparable interface,

This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method.

So if your compareTo method returns 0, for two instances of the same class, they are considered to be naturally equal by the TreeSet.

The document also says,

It is strongly recommended (though not required) that natural orderings be consistent with equals. This is so because sorted sets (and sorted maps) without explicit comparators behave "strangely" when they are used with elements (or keys) whose natural ordering is inconsistent with equals.

Though it is not defined how "strangely" it can behave.

In our case e5.equals(eTemp) is false, since the equals method is not overridden.
And e5.compareTo(eTemp) is true, so from the set's perspective e5 and eTemp are equal.

To demonstrate this you can perform the below operations:

Employee e1 = new Employee(3);
Employee e2 = new Employee(3);
set.add(e1); // gets added to the set

The below operation will return false because the set considers an equivalent of e2 already to be present in the set, though e1.equals(e2) is false, and the size of the set remains same.

System.out.println(set.add(e2));  // false

So to to be consistent, you could override the equals method, though it is not necessary.

BatScream
  • 19,260
  • 4
  • 52
  • 68
  • Wow. It was a great explanation. No confusion, no doubt, just more knowledge. Thanks :) – JPG Jan 05 '15 at 03:56