问题
I was trying to answer this question in the forum and I found that despite of overriding the equals
method in the Employee
class, I am still able to add duplicate elements to the TreeSet
.
The Javadoc of TreeSet.add(E) method says
Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false.
This essentially means that no 2 equals objects will be inserted into TreeSet
and equality is determined solely by equals()
method of contained objects.
However, below code is adding 2 elements to the Set
even though they are equal
public class Employee implements Comparable<Employee> {
String employeeName;
int employeeId;
public Employee(String name, int id) {
this.employeeName = name;
this.employeeId = id;
}
public int compareTo(Employee emp) {
//return this.employeeName.compareTo(emp.employeeName);
return (this.employeeId - emp.employeeId);
}
@Override
public String toString() {
return ("Name is: " + employeeName + " Emp id is: " + employeeId);
}
@Override
public boolean equals(Object emp) {
if(emp instanceof Employee &&((Employee)emp).employeeName.equals(this.employeeName)){
return true;
}
return false;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Set<Employee> set = new TreeSet<Employee>();
Employee e1 = new Employee("A", 1);
Employee e2 = new Employee("A", 2);
System.out.println(e1.equals(e2));
set.add(e1);
set.add(e2);
System.out.println(set);
}
}
And here is the output
true
[Name is: A Emp id is: 1, Name is: A Emp id is: 2]
Why is TreeSet
allowing multiple elements even if they are equal?
Now I changed the compareTo
method of Employee
like this
public int compareTo(Employee emp) {
return this.employeeName.compareTo(emp.employeeName);
}
And the output is
true
[Name is: A Emp id is: 1]
How the TreeSet
working properly after overriding compareTo
?
回答1:
The javadoc of TreeSet also 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. (SeeComparable
orComparator
for a precise definition of consistent with equals.) This is so because theSet
interface is defined in terms of theequals
operation, but aTreeSet
instance performs all element comparisons using itscompareTo
(orcompare
) 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 theSet
interface.
I agree that the javadoc of add()
could make it clearer that equals()
is not actually used, and re-state or link to this warning, though.
回答2:
Set
implementations use a Map
behind the scenes to manage the data and, in doing so, guarantee the uniqueness of an object through the Map
's key.
As far as I know, no explicit test of equals
is done meaning that your object would be added if it were not explicitly the same object (i.e. '==').
I think the docs are misleading but I guess that it's saying it's the formal language representation. It's ambiguous so I Could be wrong but I'm pretty sure that uniqueness is guaranteed by virtue of the underlying map's keys. FYI: A dummy value is passed with your object as the key.
回答3:
First thing about TreeSet is, it also maintain order of object while stroing them and use compareTo method.
Because in your first version of compareTo method you are using employeeId for comparison and put diffrent emoplyee id for object e1 and e2.
Treeset will treat this as a different object as comparison doesn't send zero value (it will send either + or - value).
but in second verion you implement compareTo using employeeName, which is same for object e1 and e2.
来源:https://stackoverflow.com/questions/17609419/treeset-violating-set-contract