"La méthode de comparaison viole son contrat général!"- TimSort et GridLayout
j'ai fait une palette de couleurs avec un jPanel et un tableau de JLabel dedans. Au début ça marchait bien, mais ensuite j'ai mis d'autres jLabels dans le JPanel et je les ai ajoutés quelques évènements. Maintenant, je n'arrête pas d'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.access0(EventQueue.java:103)
at java.awt.EventQueue.run(EventQueue.java:682)
at java.awt.EventQueue.run(EventQueue.java:680)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.awt.EventQueue.run(EventQueue.java:696)
at java.awt.EventQueue.run(EventQueue.java:694)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.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.access0(EventQueue.java:103)
at java.awt.EventQueue.run(EventQueue.java:682)
at java.awt.EventQueue.run(EventQueue.java:680)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.awt.EventQueue.run(EventQueue.java:696)
at java.awt.EventQueue.run(EventQueue.java:694)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain.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, j'ai eu cette erreur, mais continue de l'avoir. Quand je change la disposition de GridLayout à autre chose, alors l'erreur disparaît, mais le code devient inutile. Donc j'ai besoin de GridLayout. Quand je déplace tout dans ce JPanel à un autre JPanel, l'erreur disparaît aussi. Mais quand j'enlève le premier Composite, l'erreur revient.
soit dit en passant, le programme fonctionne, mais il n'est pas agréable de continuer à obtenir des erreurs...
Edit: quand 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...
5 réponses
il me semble que vous avez frappé un bug dans le JDK car l'erreur semble provenir des classes Swing.
Options:
-
définir le bien
java.util.Arrays.useLegacyMergeSort
commetrue
. Soit en utilisant dans votre code la ligneSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
avant tout Swing code. Comme la première ligne dans la méthode
main
devrait fonctionner.ou addition de
-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.)
-
Mise à niveau de votre JDK et voir si le problème disparaît
- revenir à la version 6 de Java
mes conclusions:
-Djava.util.Arrays.useLegacyMergeSort=true
œuvres
mais
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
ne fonctionne pas.
il est dû au fait que dans JDK tableaux.classe
static final class LegacyMergeSort {
private static final boolean userRequested = ...
il s'agit d'une variable statique définie lors du démarrage de jvm. Définir la propriété du système dans le programme n'aura aucun effet si la classe a été chargée dans jvm.
j'ai joué de surveillance le LegacyMergeSort.variable demandée par l'utilisateur, et les résultats confirmés par la déclaration ci-dessus.
mise à Jour : Le programme doit définir les propriétés du système avant java.util.Les tableaux sont chargés à classloader. Sinon, une fois chargé, le paramétrage des propriétés ne sera pas utile à cause de la raison mentionnée ci-dessus.
assurez-vous que rien d'autre ne soit chargé.classe:
en mettant le code suivant à votre programme d'essai:
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));
[mise à Jour] Cette solution n'est malheureusement pas garanti pour résoudre le problème dans tous les cas. Il ne suffit pas de corriger la politique par défaut de SortingFocusTraversalPolicy de KeyboardFocusManager.
je recommande de lire la réponse de Robin Loxley ci-dessous, y compris sa mise à jour. [/mise à Jour]
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:868)
ce problème est causé par un bug dans javax.swing.LayoutComparator
.
classe installe une version fixe de javax.swing.LayoutComparator
, qui ne viole pas le contrat de Comparator<Component>
. Cette (ou toute autre) version fixe de javax.swing.LayoutComparator
doit ê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/>
* {@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;
}
}
}
}
}
}
je viens de tomber sur la même erreur et j'ai passé un bon moment à la traquer. Pour aider les autres qui rencontrent cette erreur, il est important de savoir comment tester TimSort. Les vérifications qui violent le contrat de transitivité et jettent cette erreur sont profondes dans l'algorithme et nécessitent un test pour répondre à certains critères avant que ce problème puisse être reproduit.
- créez une liste avec 32 objets ou plus.
- dans cette liste, il faut deux ou plus.
- chaque exécution doit contenir 3 objets ou plus.
une fois que vous répondez à ces deux critères, vous pouvez commencer à tester pour cet échec.
Une course est définie comme un sous-ensemble de la liste où chaque élément est déjà dans la ordonnés.
il ne suffit pas de patch LayoutComparator comme suggéré ci-dessus. Ce correctif ne fonctionne pas dans mon cas. La question a été réglée dans JDK 8 (8u45 au moins). SortingFocusTraversalPolicy d'utiliser l'héritage de Fusion et Méthode de Tri.