Frage "Die Vergleichsmethode verletzt ihren allgemeinen Vertrag!" - TimSort und GridLayout


Ich habe eine Farbpalette mit einem jPanel und einem JLabel-Array erstellt. Am Anfang hat es gut geklappt, aber dann habe ich ein paar andere jLabels aus dem JPanel genommen und einige Events hinzugefügt. Jetzt bekomme ich diesen Fehler immer wieder:

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)

Ich habe versucht, alles zu entfernen, was ich getan habe, nachdem ich das erste Mal diesen Fehler bekommen habe, aber es immer noch bekomme. Wenn ich das Layout von GridLayout zu irgendetwas anderem ändere, verschwindet der Fehler, aber der Code wird nutzlos. Also brauche ich GridLayout. Wenn ich alles in diesem JPanel zu einem anderen JPanel verschiebe, verschwindet auch der Fehler. Aber wenn ich das erste JPanel entferne, kommt der Fehler zurück.

Übrigens, das Programm funktioniert, aber es ist nicht angenehm, Fehler zu bekommen ...

Edit: Wenn ich weniger als 225 Farbe verwende, gibt es keinen Fehler. Ich bin wirklich neugierig auf das, was passiert. Jede Erklärung würde geschätzt werden ...


37
2017-11-26 23:54


Ursprung


Antworten:


Es scheint mir, als hättest du einen getroffen Fehler im JDK Da scheint der Fehler von Swing-Klassen zu kommen.

Optionen:

  1. Definieren Sie die Eigenschaft java.util.Arrays.useLegacyMergeSort wie true. Verwenden Sie in Ihrem Code die Zeile

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

    vor jedem Swing-Code. Als erste Zeile in der main Methode sollte funktionieren.

    Oder hinzufügen

    -Djava.util.Arrays.useLegacyMergeSort=true
    

    zu Ihren Startoptionen (in der Konsole oder in den Projekteigenschaften in einer IDE, Ant-Skript usw.)

  2. Aktualisieren Sie Ihr JDK und prüfen Sie, ob das Problem behoben ist

  3. Downgrade auf Java 6

42
2017-11-27 00:55



Berichte meine Ergebnisse:

-Djava.util.Arrays.useLegacyMergeSort=true

funktioniert

aber

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

funktioniert nicht.

Es ist aufgrund der Tatsache, dass in JDK Arrays.class

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

Es ist eine statische Variable, die beim Start von jvm definiert wird. Das Festlegen der Systemeigenschaft im Programm hat keine Auswirkungen, wenn die Klasse in jvm geladen wurde.

Ich habe überwacht die Variable LegacyMergeSort.userRequested und die Ergebnisse mit der obigen Aussage bestätigt.

Aktualisieren: Das Programm muss Systemeigenschaften festlegen, bevor java.util.Arrays in den Klassenlader geladen wird. Andernfalls wird das Einstellen der Eigenschaften aus dem oben genannten Grund nicht sinnvoll sein.

Stellen Sie sicher, dass nichts anderes geladen Arrays.class:

Indem Sie Ihrem Programm folgenden Code zum Testen hinzufügen:

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

13
2017-11-09 15:35



[Aktualisieren] Diese Lösung ist leider nicht garantiert, um das Problem in allen Fällen zu lösen. Es reicht nicht aus, die standardmäßige SortingFocusTraversalPolicy zu patchen des KeyboardFocusManagers.

Ich empfehle, die Antwort von Robin Loxley unten zu lesen, einschließlich seines Updates. [/Aktualisieren]

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

Dieses Problem wird durch einen Fehler in verursacht javax.swing.LayoutComparator.

Die folgende Klasse installiert eine feste Version von javax.swing.LayoutComparator, die den Vertrag von nicht verletzt Comparator<Component>. Diese (oder jede andere) feste Version von javax.swing.LayoutComparator sollte von Oracle-Mitarbeitern an Oracle gesendet werden.

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

6
2018-02-05 17:50



Ich habe gerade den gleichen Fehler gemacht und viel Zeit damit verbracht, den Fehler aufzuspüren. Um anderen zu helfen, die auf diesen Fehler stoßen, ist es wichtig zu wissen, wie man TimSort testet. Die Überprüfungen, die den Transitivitätsvertrag verletzen und diesen Fehler werfen, sind tief im Algorithmus und erfordern einen Test, um bestimmte Kriterien zu erfüllen, bevor dieses Problem reproduziert werden kann.

  1. Erstellen Sie eine Liste mit 32 oder mehr Objekten.
  2. Innerhalb dieser Liste müssen zwei oder mehr Läufe ausgeführt werden.
  3. Jeder Lauf muss 3 oder mehr Objekte enthalten.

Sobald Sie diese beiden Kriterien erfüllen, können Sie mit dem Testen für diesen Fehler beginnen.

Ein Lauf ist als Untergruppe der Liste definiert, in der sich jedes Element bereits im gewünschten geordneten Zustand befindet.


3
2018-02-08 19:10



Es reicht nicht, LayoutComparator wie oben beschrieben zu patchen. Dieser Fix funktioniert in meinem Fall nicht. Das Problem wurde in JDK 8 behoben (mindestens 8u45). SortingFocusTraversalPolicy zur Verwendung der Legacy-Merge-Sort-Methode.


0
2018-06-16 13:17