我需要一种基于第三个对象的属性对对象集合进行排序的方法。我将尝试用一个简化的案例来描述它。
假设我们有一个 Person 对象
class Person {
String firstName;
String lastName;
...
}
我们想对与某个人相关的 Persons 集合进行排序。例如:John Doe 是我们想要找到的人,或者如果我们找不到,我们希望最“相似”的人位于排序集合的顶部。
相似性定义如下:如果只有名字匹配,那么它比只有姓氏匹配时更好。当然,如果两者都匹配,那就是宾果游戏。
我想出了一个解决方案,但我不确定它是否完美无缺。这个想法是使用如下的比较器:
public static class PersonComparator implements Comparator<Person> {
String firstName;
String lastName;
public PersonComparator(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public int compare(Person p1, Person p2) {
int p1Match = calcMatch(p1);
int p2Match = calcMatch(p2);
int result = p1Match - p2Match;
if (result == 0) {
//not very sure about this part
result = p1.firstName.compareTo(p2.firstName);
if (result == 0) {
result = p1.lastName.compareTo(p2.lastName);
}
}
return result;
}
public int calcMatch(Person p) {
StringBuilder builder = new StringBuilder();
builder.append(firstName.equals(p.firstName) ? "1" : "0");
builder.append(lastName.equals(p.lastName) ? "1" : "0");
return Integer.parseInt(builder.toString(), 2);
}
}
因此,如果第一个人的名字匹配而姓氏不匹配,他会将二进制匹配“10”转换为整数 2,而如果第二个人的名字和姓氏都匹配,则二进制值将“11”转换为 3 . compareTo 然后将简单地返回 2 - 3 = -1 表示一个“小于”然后是两个。
但是,如果两个人的名字和姓氏都与我们正在寻找的不匹配,该怎么办。匹配的“二进制值”将是相同的,返回 0 将表示两个人彼此相等(例如,至少对于 TreeSet)。当在 TreeSet 中使用这样的比较器时,两个人中只有一个人会在结果集中持续存在。
这不是期望的行为,因此,如果两人的结果相同,我会根据两人的字段比较来计算 compareTo 返回的值。
运行以下简单的测试用例显示了一个示例:
public static void main(String[] args) {
List<Person> persons = new ArrayList<Person>();
persons.add(new Person("Pietje", "Puk"));
persons.add(new Person("Jan", "Jansen"));
persons.add(new Person("John", "Doe"));
Comparator<Person> comparator = new PersonComparator("John", "Doe")
int firstCompare = comparator.compare(persons.get(0), persons.get(1));
int secondCompare = comparator.compare(persons.get(1), persons.get(2));
int thirdCompare = comparator.compare(persons.get(0), persons.get(2));
System.out.println(firstCompare + " vs " + secondCompare + " vs " + thirdCompare);
TreeSet<Person> personsSet = new TreeSet<Person>(comparator);
personsSet.addAll(persons);
personsSet.add(new Person("Baby", "Doe"));
personsSet.add(new Person("John", "Roe"));
personsSet.add(new Person("Jane", "Doe"));
int i = 0;
for (Person person : personsSet) {
System.out.println(i++ + ") " + person + " [" + comparator.calcMatch(person) + "]");
}
}
执行上面的代码会导致:
6 对 -3 对 -3
0) 扬·詹森 [0]
1) 皮耶普克 [0]
2) 母鹿宝宝 [1]
3) 简·多伊 [1]
4) 约翰·罗 [2]
5) 约翰·多伊 [3]
第一次比较基于名字(Pietje Puk vs Jan Jansen),结果为 6。第二次比较基于姓氏与枢轴(Jan Jansen vs John Doe)比较,结果为 -3,而最后一个是也基于姓氏与枢轴相比(Pietje Puk vs John Doe),结果也为 -3。
正如代码中所述,我不确定compareTo 中问题的解决方案,其中两个字段匹配相似,但具有不同的值。由于“匹配”代码始终计算 0 到 3 之间的值,因此“字段比较”可以具有更高的值,我不确定“混合”这些数字是否是个好主意。
有没有人遇到过类似的问题,或者可以确认我的解决方案符合合同并且没有缺陷?理想情况下,我希望有一个 TreeSet 可以使用的比较器,因此如果人员真的不相等,compareTo 应该只返回 0。
我想到的另一个解决方案是将“pivot”作为“普通”“Person”对象放在树集中,并根据提供给 compareTo 方法的两个人的字段使用简单的比较器。对集合进行排序后,我可以搜索枢轴对象,然后我知道它附近的元素具有最高匹配度。然而,这个解决方案听起来并不优雅,并且可能并不总是适用。