比较器与equals一致:如何? (最佳实践)

时间:2016-10-06 08:40:38

标签: java collections equals

我知道为了符合Java List<SelectListItem> userDdl = GetUserDropDown(userId, userName).ToSelectListItems(a => a.UserName, a => a.UserId.ToString(), a => a.UserId.ToString() == userIdFromDb).ToList(); 的合同(以避免意外行为),任何提供的Collections都应与equals保持一致。

假设我有这样的人:

Comparator

比较者将是

class Person {
  private String name;
  private String surname;
  private int age;


  public Person(String name, String surname, int age) {
     this.name = name;
     this.surname = surname;
     this.age = age;
  }

  public String getName() { return name; }
  public String getSurname() { return surname; }
  public int getAge() { return age; }
}

现在我想要一些排序的集合(class PersonComparator implements Comparator<Person> { @Override public int compareTo(Person p1, Person p2) { // throws NPE's return p1.getName().compareTo(p2.getName()); } } TreeMap,......无论如何),使用SortedSet仅比较Comparator的名称。 这个比较器会违反&#34;与equals&#34;合同。但是我不想覆盖Person,因为在该计划的其他部分,两个姓名,年龄相同的人可能会有所不同(如现实生活中)。

我希望在所选集合中,该名称唯一标识一个人,即equals(Object o)不能拥有&#34; John Doe&#34;和#34;约翰史密斯&#34; (一样的名字)。

就我所测试的Java Collections的当前实现而言,这很好。

我的问题是:你如何做到这一点&#34;正确&#34;,理想情况下没有违反任何合同?如果可能的话,我想避开第三方库,当然我不想自己实现数据结构只是为了摆脱合同。 我担心的是,我的代码可能会在未来的Java版本中破坏,因为它违反了合同。

2 个答案:

答案 0 :(得分:3)

您遇到的问题是设置忽略重复项。如果你说两个Person是相同的,它会将它们视为重复并删除其中一个。

return p1.getName().compareTo(p2.getName());

所以如果你有两个人叫#&#34; John&#34;作为名字,它们只会在TreeSet中出现一次。

你需要的是比较你在equals中所做的相同领域。

int cmp = p1.getName().compareTo(p2.getName());
if (cmp == 0)
    cmp = p1.getSurname().compareTo(p2.getSurname());
if (cmp == 0)
    cmp = Integer.compare(p1.getAge() - p2.getAge());
return cmp;

这样你只会考虑两个等于== true的人是重复的。

确保具有相同细节(但不同)的人不被视为重复的方法是添加唯一ID,计数器或UUID。

注意:合同不一定要保持一致,而对于BigDecimal,它并不完全一致,但在这种情况下,我没有看到你想要对待John Smith和John的充分理由Doe是同一个人。

答案 1 :(得分:1)

如果您使用的是Java 8,我建议您使用Comparator中的实用程序方法,而不是自己编写。

例如:

SortedSet<Person> people = new TreeSet<>(Comparator.comparing(Person::getName));

将实现的所有复杂性留给内置方法。它还允许您利用各种可用方法来定义多个排序条件,处理空值等。因为这些方法是API的一部分,所以您可以确信您的代码在将来的版本中不会中断。