Comparison method violates its general contract! 与 自定义比较器

Comparison method violates its general contract!

压测时发现的报错,原因是自定义的比较器没有满足可逆比较。

Comparator接口的compare方法文档中有标注:The implementor must ensure that sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y.

下面用单元测试还原一下报错场景:

public static class IntegerComparator implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        return (o1 > o2) ? 1 : -1;
    }
}

@Test
public void testIntegerComparator() {
    Integer[] arr =
    {0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 2, 1, 0, 0, 0, 2, 0, 0, 3};

    List<Integer> list = Arrays.asList(arr);
    Collections.sort(list, new IntegerComparator());
}

/*
    java.lang.IllegalArgumentException: Comparison method violates its general contract!

        at java.util.TimSort.mergeHi(TimSort.java:899)
        at java.util.TimSort.mergeAt(TimSort.java:516)
        at java.util.TimSort.mergeCollapse(TimSort.java:441)
        at java.util.TimSort.sort(TimSort.java:245)
        at java.util.Arrays.sort(Arrays.java:1438)
        at java.util.Arrays$ArrayList.sort(Arrays.java:3895)
        at java.util.Collections.sort(Collections.java:175)
*/

问题在于,return (o1 > o2) ? 1 : -1; 没有考虑相等的情况,compare(1, 1)(-1) != -compare(1, 1)(1)不符合接口要求,最后会在比较时抛错。

自定义比较器练习:BestFruitComparator

挑选最喜欢的水果,按照大小、成本、采摘时间依次进行排序,可根据PreferSize决定更喜欢大的还是小的。

Collections.sort()默认升序排序,排序后的list.get(0)是排序最小的

public class Fruit {
    // 水果名
    private String name;
    // 成本
    private String cost;
    // 大小
    private String size;
    // 采摘时间
    private Long pickTime;
}
public enum PreferSize {
    BIG("大", "BIG"),
    SMALL("小", "SMALL");

    private final String name;
    private final String code;

    PreferSize(String name, String code) {
        this.name = name;
        this.code = code;
    }
}
public class BestFruitComparator implements Comparator<Fruit> {

    private final PreferSize preferSize;

    public BestFruitComparator(PreferSize preferSize) {
        this.preferSize = preferSize;
    }

    @Override
    public int compare(Fruit o1, Fruit o2) {
        BigDecimal s1 = new BigDecimal(o1.getSize());
        BigDecimal s2 = new BigDecimal(o2.getSize());
        int result = s1.compareTo(s2);

        if (result == 0) {
            BigDecimal b1 = new BigDecimal(o1.getCost());
            BigDecimal b2 = new BigDecimal(o2.getCost());
            result = b1.compareTo(b2);
            if (result == 0) {
                // 后采摘的比较新鲜 排在前面
                result = -(o1.getPickTime().compareTo(o2.getPickTime()));
            }
            return result;
        } else if (result < 0) {
            // PreferSize为BIG Size越大越好
            return (PreferSize.BIG == preferSize) ? 1 : -1;
        } else {
            return (PreferSize.BIG == preferSize) ? -1 : 1;
        }
    }
}

构造单元测试

@Test
public void testBestFruitComparator() throws InterruptedException {
    Fruit apple0 = new Fruit("apple0", "5.5", "50", 100L);
    Fruit apple1 = new Fruit("apple1", "6.5", "60", 101L);
    Fruit apple2 = new Fruit("apple2", "6.5", "50", 102L);
    List<Fruit> fruits = new ArrayList<>();
    fruits.add(apple0);
    fruits.add(apple1);
    fruits.add(apple2);
    Collections.sort(fruits, new BestFruitComparator(PreferSize.BIG));
    for (Fruit fruit : fruits) {
        System.out.println(fruit);
    }
    Thread.sleep(100);
    Collections.sort(fruits, new BestFruitComparator(PreferSize.SMALL));
    for (Fruit fruit : fruits) {
        System.err.println(fruit);
    }
}

/*
    Fruit{name='apple1', cost='6.5', size='60', pickTime=101}
    Fruit{name='apple0', cost='5.5', size='50', pickTime=100}
    Fruit{name='apple2', cost='6.5', size='50', pickTime=102}
    Fruit{name='apple0', cost='5.5', size='50', pickTime=100}
    Fruit{name='apple2', cost='6.5', size='50', pickTime=102}
    Fruit{name='apple1', cost='6.5', size='60', pickTime=101}
*/

你可能感兴趣的:(java)