Search code examples
javacollectionsqueuecomparabletreeset

As per TreeSet two objects are shown equal but Queue shows them as unequal


I have the follwing Person Class -

Person.java -

public class Person implements Comparable<Person> {
    private int id;
    private String name;

    Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Person: Id = " + id + ", Name = " + name;
    }

    @Override
    public int compareTo(Person person) {
        int myReturn = 0;
        int minLength = 0;
        int i = 0;
        boolean equal = false;
        if (id > person.id) {
            myReturn = 1;
        } else if (id < person.id) {
            myReturn = -1;
        } else {
            if (name.length() > person.name.length()) {
                minLength = person.name.length();
            } else if (name.length() < person.name.length()) {
                minLength = name.length();
            } else {
                equal = true;
                minLength = name.length();
            }
            for (i = 0; i < minLength; i++) {
                if (name.charAt(i) > person.name.charAt(i))  {
                    myReturn = 1;
                    break;
                } else if (name.charAt(i) < person.name.charAt(i)) {
                    myReturn = -1;
                    break;
                } else {
                    continue;
                }
            }
            if (i == minLength) {
                if (equal) {
                    myReturn = 0;
                } else if (name.length() > person.name.length()) {
                    myReturn = 1;
                } else {
                    myReturn = -1;
                }
            }
        }
        return myReturn;
    }
}

Now, I have the following TreeClass instance -

TreeSet<Person> treeSet = new TreeSet<>(List.of(
                new Person(4, "Amrita"),
                new Person(4, "Amrita"),
                new Person(9, "Sunita"),
                new Person(12, "Nisha"),
                new Person(9, "Sunit"),
                new Person(9, "Sunitaa")
        ));

Upon printing -

Person: Id = 4, Name = Amrita
Person: Id = 9, Name = Sunit
Person: Id = 9, Name = Sunita
Person: Id = 9, Name = Sunitaa
Person: Id = 12, Name = Nisha

So clearly, the two Person instances - new Person(4, "Amrita") and new Person(4, "Amrita") are equal.

Now, I have the following Queue code. Since, Queue is a subinterface of Collection interface it implements all methods of Collection interface. So -

    Queue<Person> queue3 = new LinkedList<>(List.of(
                new Person(4, "Amrita"),
                new Person(2, "Suhana"),
                new Person(7, "Neha")
        ));
        Person person1 = new Person(4, "Amrita");
        Person person2 = new Person(9, "Sunita");
        System.out.println(queue3.contains(person1));
        System.out.println(queue3.contains(person2));

Ouput -

    false
    false

Thus it says that new Person(4, "Amrita") element of Queue and Object new Person(4, "Amrita") are unequal.

How is this possible?


Solution

  • You need to override @equals and @hashCode methods in the class Person.

    Explanation:

    1. You are using TreeSet. If you don't provide an explicit Comparator while creating it, it will use natural ordering (compareTo) for sorting.

    2. Whenever you @override compareTo(), it is highly recommended to @overide @equals.

    From Collections Documentation:

    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.

    To cut the long story short, since you are using TreeSet as well as other collections (List), you need to @override all three - compareTo, equals and hashCode

    PS: Whenever you override @equals - please @override hashCode too. See this.

    You can generate these methods inside IDE (Intellij / Eclipse/ VsCode etc) automatically. E.g. it would be something like this:

    @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return id == person.id && Objects.equals(name, person.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(id, name);
        }