“¡El método de comparación viola su contrato general!” – TimSort y GridLayout

Hice una paleta de colores con un jPanel y una matriz JLabel en ella. Al principio funcionó bien, pero luego puse otros jLabels fuera del JPanel y les agregué algunos eventos. Ahora sigo recibiendo este error:

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) 

Traté de eliminar todo lo que hice después de la primera vez que recibí este error, pero sigo obteniéndolo. Cuando cambio el diseño de GridLayout a cualquier otra cosa, el error desaparece, pero el código se vuelve inútil. Entonces necesito GridLayout. Cuando muevo todo en ese JPanel a otro JPanel, el error también desaparece. Pero cuando elimino el primer JPanel, vuelve el error.

Por cierto, el progtwig funciona, pero no es agradable seguir recibiendo errores …

Editar: cuando uso menos de 225 colores, no hay ningún error. Tengo mucha curiosidad sobre lo que está pasando. Cualquier explicación sería apreciada …

Me parece que has golpeado un error en el JDK ya que el error parece provenir de las clases de Swing.

Opciones:

  1. Defina la propiedad java.util.Arrays.useLegacyMergeSort como true . Ya sea usando en tu código la línea

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

    antes de cualquier código Swing. Como la primera línea en el método main debería funcionar.

    O agregando

     -Djava.util.Arrays.useLegacyMergeSort=true 

    a sus opciones de inicio (en la consola, o en las propiedades del proyecto en un IDE, script Ant, etc.)

  2. Actualice su JDK y vea si el problema desaparece

  3. Degradación a Java 6

Informe mis hallazgos:

 -Djava.util.Arrays.useLegacyMergeSort=true 

trabajos

pero

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

No funciona.

Es debido al hecho de que en JDK Arrays.class

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

Es una variable estática que se define cuando comienza jvm. Establecer la propiedad del sistema en el progtwig no tendrá efecto si la clase se ha cargado en jvm.

He estado monitoreando la variable LegacyMergeSort.userRequested y los resultados confirmados con la statement anterior.

Actualización : el progtwig debe establecer las propiedades del sistema antes de que java.util.Arrays se cargue en el cargador de clases. De lo contrario, una vez que se carga, establecer las propiedades no va a ser útil debido a la razón mencionada anteriormente.

Asegúrate de que no hay nada más cargado en Arrays.class:

Al poner el siguiente código en su progtwig para probar:

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

[Actualización] Desafortunadamente, esta solución no garantiza resolver el problema en todos los casos. No es suficiente parchear el Default SortFocus TraversalPolicy del KeyboardFocusManager.

Recomiendo leer la respuesta de Robin Loxley a continuación, incluida su actualización. [/Actualizar]

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

Este problema es causado por un error en javax.swing.LayoutComparator .

La siguiente clase instala una versión fija de javax.swing.LayoutComparator , que no infringe el contrato de Comparator . Esta (o cualquier otra) versión fija de javax.swing.LayoutComparator debe ser enviada a Oracle por algún colaborador de 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. * * 

* java.lang.IllegalArgumentException: Comparison method violates its general contract! *
*     {@code at java.util.TimSort.mergeHi(TimSort.java:868)} *

*

* Usage: call {@code Class.forName(LayoutFocusTraversalPolicyTimSortBugFixer.class.getName())} * before creating Swing components. *

* * @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}. *

* Search for 'bugfix' in the code. *

* * @author Burkhard Strauss * @since Feb 2015 */ @SuppressWarnings("serial") private static class LayoutComparator implements Comparator, 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 aAncestory = new LinkedList(); 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 bAncestory = new LinkedList(); 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 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.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; } } } } } }

Me encontré con el mismo error y pasé una buena cantidad de tiempo buscándolo. Para ayudar a otras personas que corren este error, es importante saber cómo probar TimSort. Las comprobaciones que infringen el contrato de transitividad y arrojan este error son profundas en el algoritmo y requieren una prueba para cumplir ciertos criterios antes de poder reproducir este problema.

  1. Crea una lista con 32 o más objetos.
  2. Dentro de esa lista, se necesitan dos o más carreras.
  3. Cada ejecución debe contener 3 o más objetos.

Una vez que cumpla con esos dos criterios, puede comenzar a probar esta falla.

Una ejecución se define como un subconjunto de la lista donde cada elemento ya se encuentra en el estado ordenado deseado.

No es suficiente aplicar un parche a LayoutComparator como se sugirió anteriormente. Esta solución no funciona en mi caso. El problema se solucionó en JDK 8 (8u45 al menos). SortingFocusTraversalPolicy para usar el método heredado Merge Sort.