Java Comparator 中 age 为 2 的对象为何排在 age 为 -1 前面?

为什么age为2的对象会排在age为-1对象的前面
比较类:

public class PersonComparator implements Comparator<Person> {

    @Override
    public int compare(Person o1, Person o2) {
        if (o1.name.equals("lili") || o2.name.equals("lili")) {
            return o1.age - o2.age;
        } else {
            return o1.id - o2.id;
        }
    }
}

客户端:

public static void main(String[] args) throws IOException {
    Person person = new Person("lili", 3, null, 1);
    Person person1 = new Person("aa", 2, null, 2);
    Person person3 = new Person("lili", -1, null, 3);
    Person person2 = new Person("bb", -9, null, 4);
    Person person4 = new Person("bb1", 5, null, -5);
    List<Person> collect = Stream.of(person2, person1, person, person3,person4).collect(Collectors.toList());
    List<Person> collect1 = collect.stream().sorted(new PersonComparator()).collect(Collectors.toList());
    collect1.forEach(System.out::println);
}

比较结果:

Person{name='aa', age=2, id=2, pet=null}
Person{name='bb', age=-9, id=4, pet=null}
Person{name='lili', age=-1, id=3, pet=null}
Person{name='lili', age=3, id=1, pet=null}
Person{name='bb1', age=5, id=-5, pet=null}

阅读 618
avatarAI BotBETA

在提供的 PersonComparator 实现中,排序结果出现 age=2 的对象(aa)排在 age=-1 的对象(lili)前面,原因在于比较器的逻辑不符合传递性,导致排序行为异常。以下是详细解释:

1. 问题核心:破坏比较器的传递性

  • 比较器规则

    • 如果 o1o2 的 name 是 "lili",则用 o1.age - o2.age 比较。
    • 否则,用 o1.id - o2.id 比较。
  • 问题场景

    • 比较 非 lili 对象(如 aabb):使用 id(非 lili 间正确排序)。
    • 比较 lili 和非 lili 对象(如 aalili(age=-1)):使用 age
    • 违反传递性
      假设 Aaa, id=2, age=2),Bbb, id=4, age=-9),Clili, age=-1):

      • A vs B:非 lili,A.id < B.idA 排在 B 前(2-4=-2<0)。
      • B vs C:有 lili,B.age < C.ageB 排在 C 前(-9 - (-1)=-8<0)。
      • A vs C:有 lili,A.age > C.ageA 排在 C 后(2 - (-1)=3>0,即 C 应在 A 前)。
        但排序结果中 AC 前,逻辑矛盾。

2. 排序结果分析

使用 Java Stream.sorted()(底层是 TimSort)时,因比较器规则矛盾,排序顺序不可预测。在你的输出中:

Person{name='aa', age=2, id=2}   // 非 lili,id=2
Person{name='bb', age=-9, id=4}   // 非 lili,id=4
Person{name='lili', age=-1, id=3} // lili
Person{name='lili', age=3, id=1}  // lili
Person{name='bb1', age=5, id=-5}  // 非 lili,id=-5
  • 非 lili 对象 aa(id=2)和 bb(id=4)按 id 升序排在前两位。
  • bb1(id=-5)因比较器逻辑混乱被错误地排到最后(可能因与 lili 比较时被推后)。

3. 为什么 age=2aaage=-1lili 前?

  • 当比较 aa(非 lili)和 lili(age=-1) 时:

    compare(aa, lili) → 用 age 比较:2 - (-1) = 3(正数)
    • 规则上aa 应排在 lili(age=-1) (正数表示 aa > lili)。
  • 实际结果矛盾
    因在与其他元素比较时(如 aa vs bb),aa 被推到前面,而 TimSort 在混合比较中因传递性破坏无法修正此错误,最终得到非逻辑顺序。

4. 解决方案:修复比较器

修改比较器,确保传递性:

public class FixedPersonComparator implements Comparator<Person> {
    @Override
    public int compare(Person o1, Person o2) {
        boolean isO1Lili = o1.name.equals("lili");
        boolean isO2Lili = o2.name.equals("lili");

        // 规则1:lili 排到非 lili 之后
        if (isO1Lili != isO2Lili) {
            return isO1Lili ? 1 : -1; // 非 lili 在前
        }
        // 规则2:都是 lili → 按 age 排序
        if (isO1Lili) {
            return o1.age - o2.age;
        }
        // 规则3:都是非 lili → 按 id 排序
        return o1.id - o2.id;
    }
}

修复后逻辑

  • 所有非 lili 优先(按 id 升序)。
  • 所有 lili 在后(按 age 升序)。
  • 符合传递性,排序结果可控。

总结

原始比较器破坏传递性导致排序异常,因此 age=2 的非 lili 对象被排到 age=-1 的 lili 对象前。请修复比较器规则以保证正确性。

推荐问题