web-dev-qa-db-fra.com

"La méthode de comparaison viole son contrat général!" - TimSort et GridLayout

J'ai créé une palette de couleurs contenant un tableau jPanel et un tableau JLabel. Au début, cela a bien fonctionné, mais j’ai ensuite ajouté quelques autres jLabels à JPanel et les ai ajoutés à certains événements. Maintenant, je continue à avoir cette erreur:

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)

J'ai essayé de supprimer tout ce que j'ai fait après la première fois que j'ai eu cette erreur, mais continue à l'obtenir. Lorsque je modifie la présentation de GridLayout par autre chose, l'erreur disparaît mais le code devient inutile. J'ai donc besoin de GridLayout. Lorsque je déplace tout dans ce JPanel vers un autre JPanel, l'erreur disparaît également. Mais lorsque je supprime le premier JPanel, l'erreur revient.

Au fait, le programme fonctionne, mais il n'est pas agréable de continuer à avoir des erreurs ...

Edit: Lorsque j'utilise moins de 225 couleurs, il n'y a pas d'erreur. Je suis vraiment curieux de savoir ce qui se passe. Toute explication serait appréciée ...

37
s.alem

Il me semble que vous avez frappé un bug dans le JDK puisque l'erreur semble provenir des classes Swing.

Options: 

  1. Définissez la propriété Java.util.Arrays.useLegacyMergeSort comme true. Soit en utilisant dans votre code la ligne

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

    avant tout code Swing. La première ligne de la méthode main devrait fonctionner.

    Ou en ajoutant

    -Djava.util.Arrays.useLegacyMergeSort=true
    

    à vos options de départ (dans la console ou dans les propriétés du projet dans un IDE, un script Ant, etc.)

  2. Mettez à niveau votre JDK et voyez si le problème disparaît

  3. Rétrograder à Java 6
42
madth3

Signaler mes résultats:

-Djava.util.Arrays.useLegacyMergeSort=true

travaux

mais 

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

ne marche pas.

C’est parce que dans JDK Arrays.class 

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

C'est une variable statique qui est définie quand jvm démarre. La définition de la propriété System dans le programme n'aura aucun effet si la classe a été chargée dans jvm.

J'ai surveillé la variable LegacyMergeSort.userRequested et les résultats ont été confirmés par la déclaration ci-dessus. 

Update : Le programme doit définir les propriétés système avant que Java.util.Arrays ne soit chargé dans classloader . Sinon, une fois chargé, la définition des propriétés ne sera plus utile en raison du motif mentionné au dessus de.

Assurez-vous que rien d'autre n'est chargé dans Arrays.class:

En mettant le code suivant dans votre programme pour tester:

    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
Robin Loxley

[Mise à jour] Cette solution ne permet malheureusement pas de résoudre le problème dans tous les cas. Il ne suffit pas de patcher la stratégie par défaut SortingFocusTraversalPolicy .__ du KeyboardFocusManager.

Je recommande de lire la réponse de Robin Loxley ci-dessous, y compris sa mise à jour . [/ Update]

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

Ce problème est dû à un bogue dans javax.swing.LayoutComparator.

La classe suivante installe une version corrigée de javax.swing.LayoutComparator, qui ne viole pas le contrat de Comparator<Component>. Cette version (ou toute autre version) corrigée de javax.swing.LayoutComparator devrait être soumise à Oracle par un contributeur 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;
               }
            }
         }
      }
   }
}
6
Burkhard Strauss

Je viens de rencontrer la même erreur et j'ai passé beaucoup de temps à la localiser. Pour aider les autres utilisateurs de cette erreur, il est important de savoir comment tester TimSort. Les contrôles qui violent le contrat de transitivité et génèrent cette erreur sont profondément ancrés dans l'algorithme et nécessitent un test pour répondre à certains critères avant que ce problème puisse être reproduit.

  1. Créez une liste avec 32 objets ou plus. 
  2. Dans cette liste, il doit y avoir deux courses ou plus.
  3. Chaque exécution doit contenir 3 objets ou plus.

Une fois que vous remplissez ces deux critères, vous pouvez commencer à tester cet échec. 

Une exécution est définie comme un sous-ensemble de la liste où chaque élément est déjà dans l'état ordonné souhaité.

5
Ceekay

Il ne suffit pas de patcher LayoutComparator comme suggéré ci-dessus. Ce correctif ne fonctionne pas dans mon cas . Le problème a été résolu dans JDK 8 (au moins 8u45) . SortingFocusTraversalPolicy pour utiliser la méthode de tri par fusion héritée. 

0
Dmitry F