web-dev-qa-db-ja.com

「比較方法は一般契約に違反しています!」 -TimSortおよびGridLayout

JPanelとJLabel配列を含むカラーパレットを作成しました。最初はうまくいきましたが、JPanelから他のjLabelをいくつか取り出して、イベントを追加しました。今、私はこのエラーを取得し続けます:

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を削除すると、エラーが戻ってきます。

ちなみに、プログラムは動作しますが、エラーが発生し続けるのは喜ばしいことではありません...

編集:225色未満を使用する場合、エラーはありません。私は何が起こっているのか本当に興味があります。任意の説明をいただければ幸いです...

37
s.alem

エラーはSwingクラスから発生しているように見えるため、 JDKのバグ をヒットしたように思えます。

オプション:

  1. プロパティを定義するJava.util.Arrays.useLegacyMergeSort as true。コードで次の行を使用するか

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

    swingコードの前。 mainメソッドの最初の行が機能するはずです。

    または追加

    -Djava.util.Arrays.useLegacyMergeSort=true
    

    開始オプション(コンソール、またはIDEのプロジェクトプロパティ、Antスクリプトなど)

  2. JDKをアップグレードして、問題が解決するかどうかを確認します

  3. Java 6にダウングレード
43
madth3

調査結果を報告する:

-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の起動時に定義される静的変数です。クラスがjvmにロードされている場合、プログラムでシステムプロパティを設定しても効果はありません。

LegacyMergeSort.userRequested変数を監視していて、上記のステートメントで結果を確認しました。

更新:Java.util.Arraysがクラスローダーにロードされる前に、プログラムはシステムプロパティを設定する必要があります。それ以外の場合、一度ロードすると、上記の理由により、プロパティの設定は役に立たなくなります。

Arrays.classが他にロードされていないことを確認します。

次のコードをプログラムに追加してテストします。

    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

[Update]この解決策は、残念ながらすべての場合に問題を解決することを保証するものではありません。 KeyboardFocusManagerのデフォルトのSortingFocusTraversalPolicyにパッチを当てるだけでは不十分です。

ロビン・ロクスリーのアップデートを含む以下の回答を読むことをお勧めします。 [/ Update]

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

私はちょうど同じエラーに遭遇し、それを追跡するのにかなりの時間を費やしました。このエラーに遭遇した他の人を助けるために、TimSortをテストする方法を知ることが重要です。推移契約に違反し、このエラーを投げるチェックが深いアルゴリズムであり、この問題を再現することができます前に、一定の基準を満たすためにテストが必要です。

  1. 32個以上のオブジェクトを含むリストを作成します。
  2. そのリスト内で、2回以上の実行が必要です。
  3. 各実行には3つ以上のオブジェクトが含まれている必要があります。

これらの2つの基準を満たしたら、この障害のテストを開始できます。

実行は、リストのサブセットとして定義され、各アイテムはすでに目的の順序付けされた状態になっています。

5
Ceekay

上記のsuggerestedとしてLayoutComparatorにパッチを適用するだけでは十分ではありません。この修正は、私の場合には動作しません。問題は、JDK 8(少なくとも8u45)で修正されました。 SortingFocusTraversalPolicyは、従来のマージソートメソッドを使用します。

0
Dmitry F