"Comparison method violates its general contract!",一切正常

"Comparison method violates its general contract!", everything seems OK

我知道 Java 中的比较规则(以及一般情况下),如 here 所述。
我有一个字符串数组列表。
每个 String 代表忽略花色的 Texas Hold'em Poker Hand。
每个字符串正好是 13 个字符长。
每个字符串仅由总和为 7 的数字组成。

例如,“0100300200100”代表一手扑克牌,其中包括一张 3 的牌、三张 6 的牌、两张 9 的牌和一张 Q 的牌。
(在这种情况下,这手牌代表葫芦 - 6 和 9)。

我想根据扑克牌的强弱对这个列表进行排序。
我有以下 java 代码实现了比较器的比较方法。

final Comparator<String> COMBINATION_ORDER = new Comparator<String>() {
    @Override
    public int compare(String c1, String c2) {
        if (c1.indexOf('4') != -1 || c2.indexOf('4') != -1) {  // Four of a kind
            if (c1.indexOf('4') == c2.indexOf('4')) {
                for (int i = 12; i >= 0; i--) {
                    if (c1.charAt(i) != '0' && c1.charAt(i) != '4') {
                        if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
                            return 0;
                        }
                        return 1;
                    }
                    if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
                        return -1;
                    }
                }
            }
            return c1.indexOf('4') - c2.indexOf('4');
        }
        int tripleCount1 = StringFunctions.countOccurrencesOf(c1, "3");
        int tripleCount2 = StringFunctions.countOccurrencesOf(c2, "3");
        if (tripleCount1 > 1 || (tripleCount1 == 1 && c1.indexOf('2') != -1) || tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) {  // Full house
            int higherTriple = c1.lastIndexOf('3');
            if (higherTriple == c2.lastIndexOf('3')) {
                for (int i = 12; i >= 0; i--) {
                    if (i == higherTriple) {
                        continue;
                    }
                    if (c1.charAt(i) == '2' || c1.charAt(i) == '3') {
                        if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
                            return 0;
                        }
                        return 1;
                    }
                    if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
                        return -1;
                    }
                }
            }
            return higherTriple - c2.lastIndexOf('3');
        }
        return 0;
    }
};

与此同时,我指的只是四人组和满屋。 这意味着每一手牌都将被视为彼此平等(但不如四手牌或满堂彩)。

但是当我排序的时候:

combinations.sort(COMBINATION_ORDER);

(其中 组合 是我的 ArrayList)。

我遇到异常。

Exception in thread "main" java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.TimSort.mergeLo(TimSort.java:773)
    at java.util.TimSort.mergeAt(TimSort.java:510)
    at java.util.TimSort.mergeCollapse(TimSort.java:437)
    at java.util.TimSort.sort(TimSort.java:241)
    at java.util.Arrays.sort(Arrays.java:1512)
    at java.util.ArrayList.sort(ArrayList.java:1454)
    at Poker.main(Poker.java:120)

请帮助我理解代码有什么问题。
非常感谢。

编辑:

正如@ajb 所说,我没有考虑没有满屋的三人组。

解决方案:

final Comparator<String> COMBINATION_ORDER = new Comparator<String>() {
    @Override
    public int compare(String c1, String c2) {
        if (c1.indexOf('4') != -1 || c2.indexOf('4') != -1) {  // Four of a kind
            if (c1.indexOf('4') == c2.indexOf('4')) {
                for (int i = 12; i >= 0; i--) {
                    if (c1.charAt(i) != '0' && c1.charAt(i) != '4') {
                        if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
                            return 0;
                        }
                        return 1;
                    }
                    if (c2.charAt(i) != '0' && c2.charAt(i) != '4') {
                        return -1;
                    }
                }
            }
            return c1.indexOf('4') - c2.indexOf('4');
        }
        int tripleCount1 = StringFunctions.countOccurrencesOf(c1, "3");
        int tripleCount2 = StringFunctions.countOccurrencesOf(c2, "3");
        if (tripleCount1 > 1 || (tripleCount1 == 1 && c1.indexOf('2') != -1)) {      // c1 Full house
            if (tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) {  // c2 Full house too
                int higherTriple = c1.lastIndexOf('3');
                if (higherTriple == c2.lastIndexOf('3')) {
                    for (int i = 12; i >= 0; i--) {
                        if (i == higherTriple) {
                            continue;
                        }
                        if (c1.charAt(i) == '2' || c1.charAt(i) == '3') {
                            if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {
                                return 0;
                            }
                            return 1;                                                // only c1 Full house
                        }
                        if (c2.charAt(i) == '2' || c2.charAt(i) == '3') {            // only c2 Full house
                            return -1;
                        }
                    }
                }
                return higherTriple - c2.lastIndexOf('3');
            }
            return 1;
        }
        if (tripleCount2 > 1 || (tripleCount2 == 1 && c2.indexOf('2') != -1)) {
            return -1;
        }
        return 0;
    }
};

你的比较器必须遵守的条件之一是它必须是可传递的。也就是说,如果 A > B 且 B > C,则 A > C。如果比较器不遵循此规则,则排序可能 运行 进入排序不是其预期顺序的情况,然后它会抛出异常。

您的算法中至少存在一个逻辑错误。 (可能还有其他错误,但我绝对可以发现这个错误,它肯定会导致异常。)问题是当一只手有满屋,而另一只手有 3 个同种但不是满屋。您的代码并不总能使满堂彩更大。如果 3-of-a-kind 是三张比葫芦中的三张牌大的牌,则 3-of-a-kind 比较大。所以说一只手是KKK8743,一只手是QQQ6632,一只手是JJJ8743。您的代码错误地使 KKK8743 > QQQ6632。它还说 QQQ6632 > JJJ8743。但它也说 KKK8743 = JJJ8743,因此违反了传递性。