致命异常:java.lang.IllegalArgumentException:比较方法违反了它的一般契约

Fatal Exception: java.lang.IllegalArgumentException: Comparison method violates its general contract

我知道有很多类似的问题,通过阅读这些问题的答案我得到了很大的帮助,但是我看不出我的客户是如何面对这个问题的。而且只有一个客户遇到这个问题。

我有一个列表,我正在使用 Comparator 界面对该列表进行排序。你们中有人看到以下代码有问题吗?

    private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
        @Override
        public int compare(BiologySample left, BiologySample right) {
            if (left == right || (left != null && right != null && left.getSampleDateTime() == right.getSampleDateTime())) {
                return 0;
            }

            if (left == null || left.getSampleDateTime() == null) {
                return 1;
            }

            if (right == null || right.getSampleDateTime() == null) {
                return -1;
            }

            return right.getSampleDateTime().compareTo(left.getSampleDateTime());
        }
    }

这就是我调用此函数的方式

Collections.sort(biologySamples, new BiologySamplesComparator());

我知道这种场景的主要问题是传递性。但是我不知道是什么违反了那个规则。

getSampleDateTime() 返回日期 4 月 9 日星期五 17:00:00 PDT 2021


更新 这就是我解决问题的方法。 我希望这会有所帮助,我在这个问题上被困了很长时间。

    private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
        @Override
        public int compare(BiologySample left, BiologySample right) {
            if (left == null) {
                if (right == null) {
                    return 0;
                } else {
                    return 1;
                }
            } else if (right == null) {
                return -1;
            } else if (left == right) {
                return 0;
            }
            if (left.getSampleDateTime() == null) {
                if (right.getSampleDateTime() == null) {
                    return 0;
                } else {
                    return 1;
                }
            } else if (right.getSampleDateTime() == null) {
                return -1;
            } else if (left.getSampleDateTime() == right.getSampleDateTime()) {
                return 0;
            }

            return right.getSampleDateTime().compareTo(left.getSampleDateTime());
        }
    }

null“样本”与具有空时间戳的非空样本进行比较时可能会出现不一致。

Sample a = null;
Sample b = new Sample(null);

bsc.compare(a, b); // -> 1, a > b
bsc.compare(b, a); // -> 1, b > a

首先,如果可能的话,你应该用Instant替换你的样本class中的Date,然后让你的生活更简单地说:

public static final Comparator<Sample> ORDER_BY_TIMESTAMP =
  Comparator.nullsLast(Comparator.comparing(
      Sample::getDateTime,
      Comparator.nullsLast(Comparator.naturalOrder())
  );

如果可以排除空值,就更简单了:

Comparator.comparing(Sample::getDateTime);

我在某些情况下遗漏了一些条件,这就是我解决问题的方法。

    private static class BiologySamplesComparator implements Comparator<BiologySample>, Serializable {
        @Override
        public int compare(BiologySample left, BiologySample right) {
            if (left == null) {
                if (right == null) {
                    return 0;
                } else {
                    return 1;
                }
            } else if (right == null) {
                return -1;
            } else if (left == right) {
                return 0;
            }
            if (left.getSampleDateTime() == null) {
                if (right.getSampleDateTime() == null) {
                    return 0;
                } else {
                    return 1;
                }
            } else if (right.getSampleDateTime() == null) {
                return -1;
            } else if (left.getSampleDateTime() == right.getSampleDateTime()) {
                return 0;
            }

            return right.getSampleDateTime().compareTo(left.getSampleDateTime());
        }
    }

提供