Java Comparator基于extern(第三)值

我需要一种基于第三个对象的属性对对象集合进行排序的方法.我将尝试使用简化的案例来描述它.

假设我们有一个Person对象

class Person {
    String firstName;
    String lastName;
    ...
}

我们想对一个人的一系列人物进行排序.例如: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);
    }
}

因此,如果Person的第一个名称匹配而lastname不匹配,则将二进制匹配’10’转换为整数2,而如果Person 2的first和lastnames都匹配,则二进制值将为’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 vs -3 vs -3

0) Jan Jansen [0]

1) Pietje Puk [0]

2) Baby Doe [1]

3) Jane Doe [1]

4) John Roe [2]

5) John Doe [3]

第一次比较是基于名字(Pietje Puk vs Jan Jansen)而得出的结果是6.第二次比较基于姓氏与枢轴(Jan Jansen vs John Doe)的比较,结果为-3,而最后一个是同样基于姓氏与枢轴(Pietje Puk vs John Doe)相比,也导致了-3.

正如在代码中评论的那样,我不确定compareTo中问题的解决方案,其中两个字段匹配相似,但具有不同的值.由于“匹配”代码总是计算0到3之间的值,因此“字段比较”可以有更高的值,我不确定“混合”这些数字是否是个好主意.

有没有人遇到类似的问题,或者可以确认我的解决方案是否符合合同并且没有缺陷?理想情况下,我希望有一个可以由TreeSet使用的比较器,因此,如果人们真的不相等,那么应该只返回0.

我的另一个解决方案是将’pivot’作为“普通”“Person”对象放在树集中,并使用一个简单的比较器,该比较器基于提供给compareTo方法的两个人的字段.对集合进行排序后,我可以搜索pivot对象,然后我知道它附近的元素具有最高匹配.然而,这种解决方案听起来并不优雅,并且可能并不总是适用.

解决方法:

你的问题归结为:比较器是否会产生一个总数(在精确的数学意义上)的排序?

我相信它确实如此.首先将所有值映射到0到3之间的范围.这是排序的最重要属性,因此首先对其进行测试.现在,如果它们不同,则使用整数差异来指示“完全”正常的排序.如果它们是相同的,则首先按名字排序,然后按姓氏开始按字典顺序排序.词典排序当然是完整的.所以你再好了.

正如在其他答案中所说,没有其他问题.您不必担心比较器返回的int的实际大小.

非常重要,但是你没有在这里显示,当且仅当compareTo返回0时,Person上的equals方法应该返回true.如果两个Persons具有相同的名字和姓氏,则compareTo方法只能返回0.所以,如果这是真的,那么equals也应该这样做.检查一下.好.然后是另一个方向.检查没有其他场合你的等于返回0.完成.

最后,如果你不相信你的推理,那么存在一种相当好的测试方法.创建随机人员生成器,生成人员和三人组,并测试数百万组合的总排序规则.即如果a<那么!(b< a)等等.如果我们确实遗漏了某些东西,那么这个设置的几次运行可能会指出我们推理中的缺陷.

上一篇:java – 查找比较器可能无法工作的情况


下一篇:Comparable与Comparator