“比较方法违反了它的一般合约!” – TimSort和GridLayout

我做了一个调色板,里面有一个jPanel和一个JLabel数组。 起初它工作得很好,但是后来我把一些其他jLabel从JPanel中join了一些事件。 现在我不断收到这个错误:

Exception in thread "AWT-EventQueue-0" 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) at javax.swing.SortingFocusTraversalPolicy.enumerateAndSortCycle(SortingFocusTraversalPolicy.java:136) at javax.swing.SortingFocusTraversalPolicy.getFocusTraversalCycle(SortingFocusTraversalPolicy.java:110) at javax.swing.SortingFocusTraversalPolicy.getFirstComponent(SortingFocusTraversalPolicy.java:435) at javax.swing.LayoutFocusTraversalPolicy.getFirstComponent(LayoutFocusTraversalPolicy.java:166) at javax.swing.SortingFocusTraversalPolicy.getDefaultComponent(SortingFocusTraversalPolicy.java:515) at java.awt.FocusTraversalPolicy.getInitialComponent(FocusTraversalPolicy.java:169) at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:380) at java.awt.Component.dispatchEventImpl(Component.java:4731) at java.awt.Container.dispatchEventImpl(Container.java:2287) at java.awt.Window.dispatchEventImpl(Window.java:2719) at java.awt.Component.dispatchEvent(Component.java:4687) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:723) at java.awt.EventQueue.access$200(EventQueue.java:103) at java.awt.EventQueue$3.run(EventQueue.java:682) at java.awt.EventQueue$3.run(EventQueue.java:680) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87) at java.awt.EventQueue$4.run(EventQueue.java:696) at java.awt.EventQueue$4.run(EventQueue.java:694) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.awt.EventQueue.dispatchEvent(EventQueue.java:693) at java.awt.SequencedEvent.dispatch(SequencedEvent.java:116) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:721) at java.awt.EventQueue.access$200(EventQueue.java:103) at java.awt.EventQueue$3.run(EventQueue.java:682) at java.awt.EventQueue$3.run(EventQueue.java:680) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87) at java.awt.EventQueue$4.run(EventQueue.java:696) at java.awt.EventQueue$4.run(EventQueue.java:694) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.awt.EventQueue.dispatchEvent(EventQueue.java:693) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:244) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:147) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139) at java.awt.EventDispatchThread.run(EventDispatchThread.java:97) 

我试图删除我第一次遇到这个错误后所做的一切,但仍然得到它。 当我从GridLayout更改布局到其他任何,然后错误消失,但代码变得毫无用处。 所以我需要GridLayout。 当我将JPanel中的所有内容移动到另一个JPanel时,错误也会消失。 但是,当我删除第一个JPanel,错误回来。

顺便说一句,该scheme的工作原理,但不要错过,不断收到错误…

编辑:当我使用less于225颜色,没有错误。 我真的很好奇发生了什么。 任何解释将不胜感激…

在我看来,您已经在JDK中遇到了错误,因为错误似乎来自Swing类。

选项:

  1. 将属性java.util.Arrays.useLegacyMergeSort定义为true 。 在你的代码中使用该行

     System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); 

    在任何Swing代码之前。 作为main方法的第一行应该工作。

    或者添加

     -Djava.util.Arrays.useLegacyMergeSort=true 

    到您的起始选项(在控制台中,或在IDE,Ant脚本等项目属性中)

  2. 升级您的JDK,看看问题是否消失

  3. 降级到Java 6

报告我的发现:

 -Djava.util.Arrays.useLegacyMergeSort=true 

作品

 System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); 

不起作用。

这是由于在JDK Arrays.class中的事实

  static final class LegacyMergeSort { private static final boolean userRequested = ... 

它是jvm启动时定义的一个静态variables。 如果该类已经加载到jvm中,那么在程序中设置System属性将不起作用。

我正在监视LegacyMergeSort.userRequestedvariables,并用上述语句确认了结果。

更新 :程序必须在java.util.Arrays加载到类加载器之前设置系统属性。 否则,一旦加载,由于上述原因,设置属性将不会有用。

确保没有其他的东西加载Arrays.class:

通过将下面的代码放到你的程序中来testing:

  java.lang.reflect.Method m = ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[] { String.class }); m.setAccessible(true); ClassLoader cl = ClassLoader.getSystemClassLoader(); Object test1 = m.invoke(cl, "java.util.Arrays"); System.out.println("test1 loaded? ->" + (test1 != null)); 

[更新]不幸的是,这个解决scheme并不能保证在任何情况下都能解决问题。 修补KeyboardFocusManager的默认SortingFocusTraversalPolicy是不够的。

我build议阅读下面的罗宾洛克斯利的答案,包括他的更新。 [/更新]

 java.lang.IllegalArgumentException: Comparison method violates its general contract! at java.util.TimSort.mergeHi(TimSort.java:868) 

这个问题是由javax.swing.LayoutComparator的一个错误引起的。

以下类安装javax.swing.LayoutComparator的固定版本,它不违反Comparator<Component>的约定。 这个(或任何其他) javax.swing.LayoutComparator固定版本应该由一些Oracle贡献者提交给Oracle。

 package ...; import java.awt.Component; import java.awt.ComponentOrientation; import java.awt.FocusTraversalPolicy; import java.awt.KeyboardFocusManager; import java.awt.Window; import java.lang.reflect.Field; import java.util.Comparator; import java.util.LinkedList; import java.util.ListIterator; import javax.swing.JRootPane; import javax.swing.SortingFocusTraversalPolicy; import javax.swing.UIManager; /** * Uses reflection to install a fixed version of {@link javax.swing.LayoutComparator} to solve the * LayoutFocusTraversalPolicy/TimSort problem. * * <p> * <code>java.lang.IllegalArgumentException: Comparison method violates its general contract!</code> * <br/> * &nbsp;&nbsp;&nbsp;&nbsp;{@code at java.util.TimSort.mergeHi(TimSort.java:868)} * </p> * <p> * Usage: call {@code Class.forName(LayoutFocusTraversalPolicyTimSortBugFixer.class.getName())} * before creating Swing components. * </p> * * @author Burkhard Strauss * @since Feb 2015 */ public class LayoutFocusTraversalPolicyTimSortBugFixer { static { UIManager.getUI(new JRootPane()); // make Swing install the SortingFocusTraversalPolicy final KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager .getCurrentKeyboardFocusManager(); final FocusTraversalPolicy focusTraversalPolicy = keyboardFocusManager .getDefaultFocusTraversalPolicy(); boolean fixed = false; if (focusTraversalPolicy instanceof SortingFocusTraversalPolicy) { try { final Field field = SortingFocusTraversalPolicy.class.getDeclaredField("comparator"); final boolean accessible = field.isAccessible(); try { field.setAccessible(true); field.set(focusTraversalPolicy, new LayoutComparator()); fixed = true; } finally { field.setAccessible(accessible); } } catch (final Exception e) { } } if (!fixed) { Loggers.getLoggerFor(LayoutFocusTraversalPolicyTimSortBugFixer.class).warn("could not fix the bug"); } } /** * Fixed version of {@link javax.swing.LayoutComparator}. * <p> * Search for 'bugfix' in the code. * </p> * * @author Burkhard Strauss * @since Feb 2015 */ @SuppressWarnings("serial") private static class LayoutComparator implements Comparator<Component>, java.io.Serializable { private static final int ROW_TOLERANCE = 10; private boolean horizontal = true; private boolean leftToRight = true; @SuppressWarnings("unused") void setComponentOrientation(final ComponentOrientation orientation) { horizontal = orientation.isHorizontal(); leftToRight = orientation.isLeftToRight(); } @Override public int compare(Component a, Component b) { if (a == b) { return 0; } // Row/Column algorithm only applies to siblings. If 'a' and 'b' // aren't siblings, then we need to find their most inferior // ancestors which share a parent. Compute the ancestory lists for // each Component and then search from the Window down until the // hierarchy branches. if (a.getParent() != b.getParent()) { final LinkedList<Component> aAncestory = new LinkedList<Component>(); for (; a != null; a = a.getParent()) { aAncestory.add(a); if (a instanceof Window) { break; } } if (a == null) { // 'a' is not part of a Window hierarchy. Can't cope. throw new ClassCastException(); } final LinkedList<Component> bAncestory = new LinkedList<Component>(); for (; b != null; b = b.getParent()) { bAncestory.add(b); if (b instanceof Window) { break; } } if (b == null) { // 'b' is not part of a Window hierarchy. Can't cope. throw new ClassCastException(); } for (ListIterator<Component> aIter = aAncestory.listIterator(aAncestory.size()), bIter = bAncestory .listIterator(bAncestory.size());;) { if (aIter.hasPrevious()) { a = aIter.previous(); } else { // a is an ancestor of b return -1; } if (bIter.hasPrevious()) { b = bIter.previous(); } else { // b is an ancestor of a return 1; } if (a != b) { break; } } } final int ax = a.getX(), ay = a.getY(), bx = b.getX(), by = b.getY(); int zOrder = a.getParent().getComponentZOrder(a) - b.getParent().getComponentZOrder(b); { // // Here is the bugfix: // Don't return 0 if a != b. This would violate the contract of // Comparator<Component>.compare(). // if (zOrder == 0) { zOrder = -1; } } if (horizontal) { if (leftToRight) { // LT - Western Europe (optional for Japanese, Chinese, Korean) if (Math.abs(ay - by) < ROW_TOLERANCE) { return (ax < bx) ? -1 : ((ax > bx) ? 1 : zOrder); } else { return (ay < by) ? -1 : 1; } } else { // !leftToRight // RT - Middle East (Arabic, Hebrew) if (Math.abs(ay - by) < ROW_TOLERANCE) { return (ax > bx) ? -1 : ((ax < bx) ? 1 : zOrder); } else { return (ay < by) ? -1 : 1; } } } else { // !horizontal if (leftToRight) { // TL - Mongolian if (Math.abs(ax - bx) < ROW_TOLERANCE) { return (ay < by) ? -1 : ((ay > by) ? 1 : zOrder); } else { return (ax < bx) ? -1 : 1; } } else { // !leftToRight // TR - Japanese, Chinese, Korean if (Math.abs(ax - bx) < ROW_TOLERANCE) { return (ay < by) ? -1 : ((ay > by) ? 1 : zOrder); } else { return (ax > bx) ? -1 : 1; } } } } } } 

我只是遇到了同样的错误,花了大量的时间跟踪它。 为了帮助那些遇到这个错误的人,了解如何testingTimSort非常重要。 违反传递性契约并抛出这个错误的检查在algorithm中是很深的,并且在这个问题可以被复制之前需要testing来满足一定的标准。

  1. 用32个或更多的对象创build一个列表。
  2. 在该列表中,需要两次或更多次运行。
  3. 每个运行必须包含3个或更多的对象。

一旦你符合这两个标准,你就可以开始testing这个失败了。

运行被定义为列表的子集,其中每个项目已经处于期望的有序状态。

将LayoutComparator修补为上面提到的是不够的。 此修补程序在我的情况下不起作用。 JDK 8(至less8u45)修正了这个问题。 SortingFocusTraversalPolicy使用传统的合并sorting方法。

    Interesting Posts