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

比较人违反一般合同

翟凯
2023-03-14

下面的代码是Dave Koelle的AlphanumComparator的编辑版本。编辑包含将空字符串排序到列表末尾或 JTable 底部的代码。问题是java.lang.IllegalArgumentException:比较方法违反了其一般合同!

为了解决我的问题,我调查了它并找到了诸如比较器没有返回 0 等原因; 在正确的位置。我还在Java错误数据库中发现了一条评论,上面写着

java.util.Arrays.sort和java.util.Collections.sort(间接地)使用的排序算法被替换了,新的排序实现如果检测到违反了可比较契约的可比较,可能会抛出IllegalArgumentExc的异常,之前的实现默默地忽略了这样的情况,如果需要之前的行为,可以使用新的系统属性java.util.Arrays.useLegacyMergeSort恢复之前的Mergesort行为

import java.util.Comparator;
import javax.swing.JTable;
import javax.swing.SortOrder;

public class AlphanumComparator implements Comparator<String> {
    JTable table;

    public AlphanumComparator(JTable table) {
        this.table = table;
    }

    private final boolean isDigit(char ch) {
        return ch >= 48 && ch <= 57;
    }

    private final String getChunk(String s, int slength, int marker) {
        StringBuilder chunk = new StringBuilder();
        char c = s.charAt(marker);
        chunk.append(c);
        marker++;
        if (isDigit(c)) {
            while (marker < slength) {
                c = s.charAt(marker);
                if (!isDigit(c))
                    break;
                chunk.append(c);
                marker++;
            }
        } else {
            while (marker < slength) {
                c = s.charAt(marker);
                if (isDigit(c))
                    break;
                chunk.append(c);
                marker++;
            }
        }
        return chunk.toString();
    }

    public int compare(String s1, String s2) {
        boolean swapInt = table.getRowSorter().getSortKeys().get(0).getSortOrder() == SortOrder.ASCENDING;

        int thisMarker = 0;
        int thatMarker = 0;
        int s1Length = s1.length();
        int s2Length = s2.length();

        if(s1Length != 0 && s2Length != 0) {
            while (thisMarker < s1Length && thatMarker < s2Length) {
                String thisChunk = getChunk(s1, s1Length, thisMarker);
                thisMarker += thisChunk.length();

                String thatChunk = getChunk(s2, s2Length, thatMarker);
                thatMarker += thatChunk.length();

                int result = 0;
                if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) {
                    int thisChunkLength = thisChunk.length();
                    result = thisChunkLength - thatChunk.length();
                    if (result == 0) {
                        for (int i = 0; i < thisChunkLength; i++) {
                            result = thisChunk.charAt(i) - thatChunk.charAt(i);
                            if (result != 0) {
                                return result;
                            }
                        }
                    }
                } else {
                    result = thisChunk.compareTo(thatChunk);
                }

                if (result != 0)
                    return result;
            }

            return s1Length - s2Length;
        } else {
            if(swapInt) {
                if(s1Length == 0) {
                    return 1;
                } else {
                    return -1;
                }
            } else {
                if(s1Length == 0) {
                    return -1;
                } else {
                    return 1;
                }
            }
        }
    }
}

有人能够帮助解决我的问题并解释为什么这个比较器违反了可比合同吗

异常堆栈跟踪(如果需要)

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Comparison method violates its general contract!
    at java.util.ComparableTimSort.mergeLo(ComparableTimSort.java:744)
    at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:481)
    at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:422)
    at java.util.ComparableTimSort.sort(ComparableTimSort.java:222)
    at java.util.Arrays.sort(Arrays.java:1246)
    at javax.swing.DefaultRowSorter.sort(DefaultRowSorter.java:607)
    at javax.swing.DefaultRowSorter.setSortKeys(DefaultRowSorter.java:319)
    at javax.swing.DefaultRowSorter.toggleSortOrder(DefaultRowSorter.java:480)
    at javax.swing.plaf.basic.BasicTableHeaderUI$MouseInputHandler.mouseClicked(BasicTableHeaderUI.java:112)
    at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:270)
    at java.awt.Component.processMouseEvent(Component.java:6538)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
    at java.awt.Component.processEvent(Component.java:6300)
    at java.awt.Container.processEvent(Container.java:2236)
    at java.awt.Component.dispatchEventImpl(Component.java:4891)
    at java.awt.Container.dispatchEventImpl(Container.java:2294)
    at java.awt.Component.dispatchEvent(Component.java:4713)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4534)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
    at java.awt.Container.dispatchEventImpl(Container.java:2280)
    at java.awt.Window.dispatchEventImpl(Window.java:2750)
    at java.awt.Component.dispatchEvent(Component.java:4713)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
    at java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.awt.EventQueue$3.run(EventQueue.java:709)
    at java.awt.EventQueue$3.run(EventQueue.java:703)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
    at java.awt.EventQueue$4.run(EventQueue.java:731)
    at java.awt.EventQueue$4.run(EventQueue.java:729)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

共有2个答案

漆雕誉
2023-03-14

您的比较器:

if (s1Length != 0 && s2Length != 0) {
    ...
} else {
    if (swapInt) {
        if(s1Length == 0) {
            return 1;
        } else {
            return -1;
        }
    } else {
        if(s1Length == 0) {
            return -1;
        } else {
            return 1;
        }
     }
}

因此,如果它没有输入if块,这意味着至少有一个字符串是空的。但是两者都可能是空的。但是在这种情况下,您的比较器只返回-1或1。这意味着如果A和B都是空的,并且将A与B进行比较会导致-1,那么将B与A进行比较也会导致-1,因此A既比B小又比B大。

只需开始你的else块

if (s1Length == 0 && s2Length == 0) {
    return 0;
}
郝冥夜
2023-03-14

我认为问题在于当s1L的为零时,您的代码从不检查s2L的。您需要添加另一个检查以查看两个字符串是否都为空,如下所示:

if(swapInt) {
    if(s1Length == 0 && s2Length != 0) {
        return 1;
    } else if (s2Length == 0 && s1Length != 0) {
        return -1;
    } else {
        return 0;
    }
} else {
    if(s1Length == 0 && s2Length != 0) {
        return -1;
    } else if (s2Length == 0 && s1Length != 0) {
        return 1;
    } else {
        return 0;
    }
}

您当前的实现返回1-1,即使两个字符串为空(这意味着它们必须比较为相等并返回零)。新的排序算法检测到此问题,并抛出异常。

注:

您应该能够通过使 swapInt 成为 1 或 -1int 来进一步简化此代码,具体取决于 getSortOrder 结果:

if(s1Length == 0 && s2Length != 0) {
    return swapInt;
} else if (s2Length == 0 && s1Length != 0) {
    return -swapInt;
} else {
    return 0;
}
 类似资料:
  • 我目前正在Java中对集合进行排序。我收到了错误消息“比较方法违反了它的一般契约”。我也理解这个错误消息,但我(主要)使用Long类型的构建比较方法。所以我不知道,在这种情况下,排序方法仍然违反了契约。这是我的代码: 这里是错误:

  • 下面是导致异常的代码块,如所示, 代码: 例外情况: 当我将相同的代码作为独立程序运行时,该问题从未出现。这里的比较器有什么问题?有没有办法在独立代码中重现该问题? 这个问题只在Java 1.7上出现,因为Arrays.sort上的实现发生了变化

  • 我在尝试对节点的数组列表进行排序时遇到了这个错误。我尝试了大多数解决方案,但没有一个在我的案例中有效。 此代码为 它适用于小输入,但是当输入数量很大时,它会给出这个错误。我也读过比较方法中的传递性规则,但我不知道它是如何在这种情况下应用的。 先谢谢你。

  • 作为实习的一部分,我被要求调查一个错误。一段代码正在抛出 Java . lang . illegalargumentexception:比较法违反了它的通用契约! 自定义通过查看自定义类的成员变量来比较两个自定义类: 此自定义类的 方法查看此自定义类的 成员变量。我们很难重现这种行为。我的下意识反应是将自定义中的 return 语句替换为 ,但我的团队怀疑这会解决问题。谁能提供任何见解?

  • 我有一个类字段,和。我需要使用对它们进行排序,但我得到了一个异常: java.lang.IllegalArgumentException:比较方法违反了它的一般约定! 我的< code>compareTo方法: 请帮我找出compareTo方法中的错误。谢了。

  • 我看到我的应用程序在一些中国 Android 手机上发生了很多崩溃,并出现错误:比较方法违反了其总合同! 我读过这与Collections.sort有关。 我不太确定的是,这是否是因为我的自定义比较器。 以下是错误发生的地方: 比较器是这样的: 所以我不太确定比较器是否搞砸了什么,或者我是否需要以不同的方式进行collections.sort调用 感谢任何帮助