Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8134576

Comparison method contract violation not always detected

XMLWordPrintable

      FULL PRODUCT VERSION :
      java version "1.8.0_51"
      Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
      Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)


      ADDITIONAL OS VERSION INFORMATION :
      Linux 2.6.18-274.3.1.el5 #1 SMP x86_64 x86_64 x86_64 GNU/Linux

      A DESCRIPTION OF THE PROBLEM :
      The "Illegal Argument Exception" thrown for violating the Comparable interface contract by Collections.sort is inconsistent.
      If you violate the Comparable interface with a class but...
      You have fewer than 32 items in your list, no exception is thrown.
      You have more than 32 items in your list, but they are distributed in certain ways, no exception is thrown.

      It should either throw the exception every time you sort with a class that violates the Comparable contract or not at all.

      We have a function that violates the Comparable interface in a very subtle way. In order to actually get the exception, there needed to be a certain pattern with regards to what we were sorting. If the exact same objects were already sorted and we called sort on them again, no exception was thrown even though the compareTo function violates the contract.

      It just so happens that this "pattern" of objects only happened in production and bit us pretty good.

      I would like it if the behavior of the Collections.sort function was consistent with regards to throwing the IllegalArgumentException when a class violates the Comparable contract.

      REGRESSION. Last worked in version 6u43

      ADDITIONAL REGRESSION INFORMATION:
      java version "1.6.0_27"
      Java(TM) SE Runtime Environment (build 1.6.0_27-b07)
      Java HotSpot(TM) 64-Bit Server VM (build 20.2-b06, mixed mode)


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Create a class that implements the Comparable interface and always return -1 from the compareTo function. Call Collections.sort on a list with less than 32 of those objects.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      An exception should be thrown because the compareTo function violates the Comparable contract because the sign of x.compareTo(y) is the same as the sign as y.compareTo(x).
      ACTUAL -
      No exception is thrown.

      In my opinion, it is fine if no exception thrown, but it should be consistent. The Collections.sort function should either always throw an exception when a function violates the Comparable contract or never throw it.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      java.lang.IllegalArgumentException: Comparison method violates its general contract!
              at java.util.ComparableTimSort.mergeHi(ComparableTimSort.java:862)
              at java.util.ComparableTimSort.mergeAt(ComparableTimSort.java:479)
              at java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.java:418)
              at java.util.ComparableTimSort.sort(ComparableTimSort.java:218)
              at java.util.Arrays.sort(Arrays.java:1246)

      REPRODUCIBILITY :
      This bug can be reproduced occasionally.

      ---------- BEGIN SOURCE ----------
      import java.util.*;

      //Run with arg0 less than 32, no exception is ever thrown.
      //Run with arg0 higher than 32, an exception is always thrown.

      public class CompareTest
      {
      public static void main(String[] args)
      {int size = Integer.parseInt(args[0]);

      CompareTest compareTest = new CompareTest();
      boolean result = compareTest.test(size);

      if (result)
      {System.out.println("Exception thrown!");
      }
      else
      {System.out.println("No exception thrown!");
      }
      }

      public boolean test(int size)
      {
      List<TestObject> requests = new ArrayList<TestObject>();

      for (int index = 0; index < size; index++)
      {TestObject request = new TestObject();
      request.value = index;
      requests.add(request);
      }

      try
      {Collections.sort(requests);
      }
      catch (Exception e)
      {System.out.println("Exception while sorting!");
      e.printStackTrace();
      return true;
      }

      return false;
      }

      }

      class TestObject implements Comparable<TestObject>
      {public int value;

      public int compareTo(TestObject req)
      {
      if (value % 3 == 0)
      {return -1;
      }
      else if (value % 3 == 1)
      {return 0;
      }

      return 1;
      }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      There is a workaround already mentioned which involves setting the "java.util.Arrays.useLegacyMergeSort" system property and we can also downgrade to java 6.

      We are going to downgrade to java 6.

            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: