使用比较器时从 TreeSet 丢失的数据
Data lost from TreeSet when using Comparator
我有以下代码,可以根据他们的经验对 Employees's
进行排序。
我要添加 2 名 name
和 experience
相同的员工。我预计最后 set
将有 2 名员工,但我只有一名。
我也覆盖了equals
和hashcode
,谁能告诉我为什么我只得到一个员工。
测试Class
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.junit.Test;
public class SetWithComparator {
@Test
public void testComparatorWithSet() {
Comparator<Employee> comparator =
(emp1, emp2) -> emp1.getYearOFExp().compareTo(emp2.getYearOFExp());
Set<Employee> empSet = new TreeSet<>(comparator);
Employee e1 = new Employee();
e1.setName("Employee-1");
e1.setYearOFExp(12f);
Employee e2 = new Employee();
e2.setName("Employee-2");
e2.setYearOFExp(12f);
empSet.add(e1);
empSet.add(e2);
}
}
型号Class
class Employee {
private String name;
private Float yearOFExp;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getYearOFExp() {
return yearOFExp;
}
public void setYearOFExp(Float yearOFExp) {
this.yearOFExp = yearOFExp;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Employee) {
Employee e = (Employee) obj;
return new EqualsBuilder().append(name, e.getName()).isEquals();
} else {
return false;
}
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(name).toHashCode();
}
}
因为比较器和你的equals方法不一致。请查看 Comparator.
的文档
The ordering imposed by a comparator c on a set of elements S is said
to be consistent with equals if and only if c.compare(e1, e2)==0 has
the same boolean value as e1.equals(e2) for every e1 and e2 in S.
Caution should be exercised when using a comparator capable of
imposing an ordering inconsistent with equals to order a sorted set
(or sorted map). Suppose a sorted set (or sorted map) with an explicit
comparator c is used with elements (or keys) drawn from a set S. If
the ordering imposed by c on S is inconsistent with equals, the sorted
set (or sorted map) will behave "strangely." In particular the sorted
set (or sorted map) will violate the general contract for set (or
map), which is defined in terms of equals.
Comparable 的文档中暗示了您所经历的确切行为(尽管您使用了比较器):
For example, if one adds two keys a and b such that (!a.equals(b) &&
a.compareTo(b) == 0) to a sorted set that does not use an explicit
comparator, the second add operation returns false (and the size of
the sorted set does not increase) because a and b are equivalent from
the sorted set's perspective.
在您的情况下:comparator.compare(e1, e2)
是 0
,e1.equals(e2)
是 false
。
对于SortedSet
,Comparator
决定了哪些元素是相同的,它不会包含重复项。如果你不想把所有有相同经验的员工都认为是相同的,你必须添加一个二次排序:
Comparator<Employee> comparator = Comparator.comparing(Employee::getYearOFExp)
.thenComparing(Employee::getName);
请注意,您必须包含构成员工身份的所有 属性。在您的示例中,只有名称,但是在现实生活中您会拥有更多名称。另一方面,如果您有确定身份的 ID,则无需检查其他属性,事实上,不应该,因为大多数属性,包括名字,可以改变。这也适用于 equals
和 hashCode
.
的实施
为了警告关于什么可以被授予的错误假设,性别变化是一个现实生活中的事实,甚至生日也可能是假的,需要更正。
我有以下代码,可以根据他们的经验对 Employees's
进行排序。
我要添加 2 名 name
和 experience
相同的员工。我预计最后 set
将有 2 名员工,但我只有一名。
我也覆盖了equals
和hashcode
,谁能告诉我为什么我只得到一个员工。
测试Class
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.junit.Test;
public class SetWithComparator {
@Test
public void testComparatorWithSet() {
Comparator<Employee> comparator =
(emp1, emp2) -> emp1.getYearOFExp().compareTo(emp2.getYearOFExp());
Set<Employee> empSet = new TreeSet<>(comparator);
Employee e1 = new Employee();
e1.setName("Employee-1");
e1.setYearOFExp(12f);
Employee e2 = new Employee();
e2.setName("Employee-2");
e2.setYearOFExp(12f);
empSet.add(e1);
empSet.add(e2);
}
}
型号Class
class Employee {
private String name;
private Float yearOFExp;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Float getYearOFExp() {
return yearOFExp;
}
public void setYearOFExp(Float yearOFExp) {
this.yearOFExp = yearOFExp;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Employee) {
Employee e = (Employee) obj;
return new EqualsBuilder().append(name, e.getName()).isEquals();
} else {
return false;
}
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(name).toHashCode();
}
}
因为比较器和你的equals方法不一致。请查看 Comparator.
的文档The ordering imposed by a comparator c on a set of elements S is said to be consistent with equals if and only if c.compare(e1, e2)==0 has the same boolean value as e1.equals(e2) for every e1 and e2 in S.
Caution should be exercised when using a comparator capable of imposing an ordering inconsistent with equals to order a sorted set (or sorted map). Suppose a sorted set (or sorted map) with an explicit comparator c is used with elements (or keys) drawn from a set S. If the ordering imposed by c on S is inconsistent with equals, the sorted set (or sorted map) will behave "strangely." In particular the sorted set (or sorted map) will violate the general contract for set (or map), which is defined in terms of equals.
Comparable 的文档中暗示了您所经历的确切行为(尽管您使用了比较器):
For example, if one adds two keys a and b such that (!a.equals(b) && a.compareTo(b) == 0) to a sorted set that does not use an explicit comparator, the second add operation returns false (and the size of the sorted set does not increase) because a and b are equivalent from the sorted set's perspective.
在您的情况下:comparator.compare(e1, e2)
是 0
,e1.equals(e2)
是 false
。
对于SortedSet
,Comparator
决定了哪些元素是相同的,它不会包含重复项。如果你不想把所有有相同经验的员工都认为是相同的,你必须添加一个二次排序:
Comparator<Employee> comparator = Comparator.comparing(Employee::getYearOFExp)
.thenComparing(Employee::getName);
请注意,您必须包含构成员工身份的所有 属性。在您的示例中,只有名称,但是在现实生活中您会拥有更多名称。另一方面,如果您有确定身份的 ID,则无需检查其他属性,事实上,不应该,因为大多数属性,包括名字,可以改变。这也适用于 equals
和 hashCode
.
为了警告关于什么可以被授予的错误假设,性别变化是一个现实生活中的事实,甚至生日也可能是假的,需要更正。