当前位置: 首页 > 知识库问答 >
问题:

为什么这段代码抛出异常-比较方法违反了它的一般合同

艾文斌
2023-03-14

我使用了下面的代码,它抛出了一个IllegalArgumentException:

// Copyright (c) 2003-2014, Jodd Team (jodd.org). All Rights Reserved.

package jodd.util;

import java.util.Comparator;

/**
 * Compares two strings in natural, alphabetical, way.
 */
public class NaturalOrderComparator<T> implements Comparator<T> {

    protected final boolean ignoreCase;

    public NaturalOrderComparator() {
        ignoreCase = false;
    }

    public NaturalOrderComparator(boolean ignoreCase) {
        this.ignoreCase = ignoreCase;
    }

    /**
     * Compare digits at certain position in two strings.
     * The longest run of digits wins. That aside, the greatest
     * value wins.
     */
    protected int compareDigits(String str1, int ndx1, String str2, int ndx2) {
        int bias = 0;

        while (true) {
            char char1 = charAt(str1, ndx1);
            char char2 = charAt(str2, ndx2);

            boolean isDigitChar1 = CharUtil.isDigit(char1);
            boolean isDigitChar2 = CharUtil.isDigit(char2);

            if (!isDigitChar1 && !isDigitChar2) {
                return bias;
            }
            if (!isDigitChar1) {
                return -1;
            }
            if (!isDigitChar2) {
                return 1;
            }

            if (char1 < char2) {
                if (bias == 0) {
                    bias = -1;
                }
            } else if (char1 > char2) {
                if (bias == 0) {
                    bias = 1;
                }
            } else if (char1 == 0 && char2 == 0) {
                return bias;
            }

            ndx1++;
            ndx2++;
        }
    }

    public int compare(T o1, T o2) {
        String str1 = o1.toString();
        String str2 = o2.toString();

        int ndx1 = 0, ndx2 = 0;
        int zeroCount1, zeroCount2;
        char char1, char2;

        int result;

        while (true) {
            // only count the number of zeroes leading the last number compared
            zeroCount1 = zeroCount2 = 0;

            char1 = charAt(str1, ndx1);
            char2 = charAt(str2, ndx2);

            // skip over leading spaces or zeros in both strings

            while (Character.isSpaceChar(char1) || char1 == '0') {
                if (char1 == '0') {
                    zeroCount1++;
                } else {
                    zeroCount1 = 0; // counts only last 0 prefixes, space char interrupts the array of 0s
                }
                ndx1++;
                char1 = charAt(str1, ndx1);
            }

            while (Character.isSpaceChar(char2) || char2 == '0') {
                if (char2 == '0') {
                    zeroCount2++;
                } else {
                    zeroCount2 = 0;
                }
                ndx2++;
                char2 = charAt(str2, ndx2);
            }

            // process digits

            boolean isDigitChar1 = CharUtil.isDigit(char1);
            boolean isDigitChar2 = CharUtil.isDigit(char2);

            if (isDigitChar1 && isDigitChar2) {
                result = compareDigits(str1, ndx1, str2, ndx2);
                if (result != 0) {
                    // not equals, return
                    return result;
                }
                // equal numbers
                if (zeroCount1 != zeroCount2) {
                    return zeroCount1 - zeroCount2;
                }
            }

            if (char1 == 0 && char2 == 0) {
                // the end; the strings are the same, maybe compare ascii?
                return zeroCount1 - zeroCount2;
            }

            // check when one of the numbers is just zeros
            if (isDigitChar1 || isDigitChar2) {
                if (zeroCount1 != zeroCount2) {
                    return zeroCount2 - zeroCount1;
                }
            }

            // checks when both numbers are zero
            if (zeroCount1 != zeroCount2) {
                return zeroCount1 - zeroCount2;
            }

            // compare chars
            if (ignoreCase) {
                char1 = Character.toLowerCase(char1);
                char2 = Character.toLowerCase(char2);
            }
            if (char1 < char2) {
                return -1;
            }
            if (char1 > char2) {
                return 1;
            }

            ndx1++;
            ndx2++;
        }
    }

    /**
     * Safe charAt.
     */
    private static char charAt(String s, int i) {
        if (i >= s.length()) {
            return 0;
        }
        return s.charAt(i);
    }
}

抛出异常

java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.TimSort.mergeLo(TimSort.java:747)
    at java.util.TimSort.mergeAt(TimSort.java:483)
    at java.util.TimSort.mergeCollapse(TimSort.java:410)
    at java.util.TimSort.sort(TimSort.java:214)
    at java.util.TimSort.sort(TimSort.java:173)
    at java.util.Arrays.sort(Arrays.java:659)
    at java.util.Collections.sort(Collections.java:217)

这由以下函数调用:

@Override
    public int compare(final T o1, final T o2) {
        int result;
        final MyObject obj1 = (MyObject) o1;
        final MyObject obj2 = (MyObject) o2;

                   return     result = compareStringId(obj1.getStringId(),obj2.getStringId());           

    }

private int compareStringId(final String Id1, final String Id2) {
        return super.compare((T) Id1, (T) Id2);
    }

它在我们的本地机器上运行良好,但在生产上失败了,我们无法连接到机器来找出原因。你能帮忙吗

共有1个答案

郝哲茂
2023-03-14

问题在于比较器的实现不正确。根据 Java 文档,比较器必须既是自反的又是可传递的。在这种情况下,不能保证传递性。在之前的Java 8之前,这不是一个大问题,即排序实现(MergeSort)不会抛出异常。Java8 将默认排序实现更改为 TimSort,这对具有无效合约的比较器更加敏感,因此它可能会抛出异常。

但是,这对您解决问题没有多大帮助。如何在此处检查同一类的最新版本。它已升级以支持比较器合约,而且在某些边缘情况下效果更好,更不用说对重音的初始支持了。

 类似资料:
  • 我认为下面的comprator实现是错误的。它会给我一个字符串的排序。但当我使用这个实现对List进行排序时,它不会抛出以下异常:“java.lang.IllegalArgumentException:Comparison方法违反了它的一般约定!” 问:为什么上面的异常不是由 Collections.sort(list, new WrongComparator()) 方法调用抛出的?

  • 可能的重复: 为什么我的比较方法会抛出异常 — 比较方法违反了其一般合同! 我有这个代码: 有时它会引发以下异常: 为什么? 1) 我该如何避免呢?2) 我怎么能抓住这个例外? 提前谢谢。

  • 对不起,又是一个问题,但我根本不明白这里发生了什么,或者我哪里出错了。 概述:我有一个包含多个-对象。它们有一个和一个。我想按以下条件对它们进行排序,优先级如下所列: 但是当这样做时,我得到以下错误: 我得到的:我在JavaFX项目中也有同样的事情发生,但是在那里我使用而不是。我需要转换为,因为显然Android还不支持方法。 这是启动排序和定义< code>Comperator的方法: 如您所见

  • 下面的代码在Java 6上运行时抛出“Java比较方法违反了它的通用契约”。但是,在Java 7上,它不会抛出同样的异常。有什么问题?。如何修改这段代码以在Java 7上抛出异常?。

  • 我在整理名单 排序代码: FinalSentence类标头: compareTo()实现: 这是例外: 对于一个小列表(少于 50 个元素),它可以工作。对于一个大型列表(它也应该与那些一起使用),它会引发此异常。列表的实例类型是 ArrayList,这并不重要。 我不知道如何深入了解这一点。列表已满,元素类型相同(那里没有多态性),但是对于大型列表,我得到了这个奇怪的例外。 有什么想法吗? 谢谢

  • 下面是导致异常的代码块, 代码: 例外情况: 当我运行与独立程序相同的代码时,问题永远不会发生。这里的比较器有什么问题?有没有办法在独立代码中重现该问题? 此问题仅在Java 1.7上发生,因为Arrays.sort上的实现已发生更改