“比较方法违反了它的总契约!”,一切似乎都没问题

时间:2015-05-26 04:30:08

标签: java collections compare comparator comparable

我已经了解了here所述的Java(和一般)中的比较规则 我有一个字符串的ArrayList 每个字符串代表德州扑克手牌,忽略该套装 每个字符串长度正好为13个字符 每个字符串由仅为7的数字组成。

例如,“0100300200100”代表扑克牌,其中包括一张3张牌,3张6张牌,2张9张牌和1张皇后牌。
(在这种情况下,手代表满堂红 - 六分之一的九分)。

我想根据扑克牌的实力对这份名单进行排序 我有以下java代码实现Comparator的比较方法。

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所说,我没有考虑到没有满屋的三种类型。

SOLUTION:

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;
    }
};

1 个答案:

答案 0 :(得分:2)

比较器必须遵守的一个条件是它必须是可传递的。也就是说,如果A> B和B&gt; C,然后A> C.如果比较器不遵循此规则,则排序可能会遇到排序不符合预期的情况,然后会抛出异常。

您的算法中至少存在一个逻辑错误。 (可能还有其他错误,但我肯定能发现这个错误,它肯定会导致异常。)问题是当一只手有一个满屋子,而另一只手有3种而不是满屋子。你的代码并不总是让整个房子更大。如果3个牌中的三张牌比满堂红牌中的三张牌高三张,那么3张牌将比较大。所以说一方面是KKK8743,另一方面是QQQ6632,另一方面是JJJ8743。您的代码错误地使KKK8743&gt; QQQ6632。它还说QQQ6632&gt; JJJ8743。但它也说KKK8743 = JJJ8743,因此违反了传递性。