“Comparison method violates its general contract!”

前端 未结 11 2070
梦如初夏
梦如初夏 2020-11-21 06:07

Can someone explain me in simple terms, why does this code throw an exception, \"Comparison method violates its general contract!\", and how do I fix it?

pri         


        
相关标签:
11条回答
  • 2020-11-21 06:40

    The violation of the contract often means that the comparator is not providing the correct or consistent value when comparing objects. For example, you might want to perform a string compare and force empty strings to sort to the end with:

    if ( one.length() == 0 ) {
        return 1;                   // empty string sorts last
    }
    if ( two.length() == 0 ) {
        return -1;                  // empty string sorts last                  
    }
    return one.compareToIgnoreCase( two );
    

    But this overlooks the case where BOTH one and two are empty - and in that case, the wrong value is returned (1 instead of 0 to show a match), and the comparator reports that as a violation. It should have been written as:

    if ( one.length() == 0 ) {
        if ( two.length() == 0 ) {
            return 0;               // BOth empty - so indicate
        }
        return 1;                   // empty string sorts last
    }
    if ( two.length() == 0 ) {
        return -1;                  // empty string sorts last                  
    }
    return one.compareToIgnoreCase( two );
    
    0 讨论(0)
  • 2020-11-21 06:40

    In my case I was doing something like the following:

    if (a.someField == null) {
        return 1;
    }
    
    if (b.someField == null) {
        return -1;
    }
    
    if (a.someField.equals(b.someField)) {
        return a.someOtherField.compareTo(b.someOtherField);
    }
    
    return a.someField.compareTo(b.someField);
    

    What I forgot to check was when both a.someField and b.someField are null.

    0 讨论(0)
  • 2020-11-21 06:43

    Your comparator is not transitive.

    Let A be the parent of B, and B be the parent of C. Since A > B and B > C, then it must be the case that A > C. However, if your comparator is invoked on A and C, it would return zero, meaning A == C. This violates the contract and hence throws the exception.

    It's rather nice of the library to detect this and let you know, rather than behave erratically.

    One way to satisfy the transitivity requirement in compareParents() is to traverse the getParent() chain instead of only looking at the immediate ancestor.

    0 讨论(0)
  • 2020-11-21 06:43

    Java does not check consistency in a strict sense, only notifies you if it runs into serious trouble. Also it does not give you much information from the error.

    I was puzzled with what's happening in my sorter and made a strict consistencyChecker, maybe this will help you:

    /**
     * @param dailyReports
     * @param comparator
     */
    public static <T> void checkConsitency(final List<T> dailyReports, final Comparator<T> comparator) {
      final Map<T, List<T>> objectMapSmallerOnes = new HashMap<T, List<T>>();
    
      iterateDistinctPairs(dailyReports.iterator(), new IPairIteratorCallback<T>() {
        /**
         * @param o1
         * @param o2
         */
        @Override
        public void pair(T o1, T o2) {
          final int diff = comparator.compare(o1, o2);
          if (diff < Compare.EQUAL) {
            checkConsistency(objectMapSmallerOnes, o1, o2);
            getListSafely(objectMapSmallerOnes, o2).add(o1);
          } else if (Compare.EQUAL < diff) {
            checkConsistency(objectMapSmallerOnes, o2, o1);
            getListSafely(objectMapSmallerOnes, o1).add(o2);
          } else {
            throw new IllegalStateException("Equals not expected?");
          }
        }
      });
    }
    
    /**
     * @param objectMapSmallerOnes
     * @param o1
     * @param o2
     */
    static <T> void checkConsistency(final Map<T, List<T>> objectMapSmallerOnes, T o1, T o2) {
      final List<T> smallerThan = objectMapSmallerOnes.get(o1);
    
      if (smallerThan != null) {
        for (final T o : smallerThan) {
          if (o == o2) {
            throw new IllegalStateException(o2 + "  cannot be smaller than " + o1 + " if it's supposed to be vice versa.");
          }
          checkConsistency(objectMapSmallerOnes, o, o2);
        }
      }
    }
    
    /**
     * @param keyMapValues 
     * @param key 
     * @param <Key> 
     * @param <Value> 
     * @return List<Value>
     */ 
    public static <Key, Value> List<Value> getListSafely(Map<Key, List<Value>> keyMapValues, Key key) {
      List<Value> values = keyMapValues.get(key);
    
      if (values == null) {
        keyMapValues.put(key, values = new LinkedList<Value>());
      }
    
      return values;
    }
    
    /**
     * @author Oku
     *
     * @param <T>
     */
    public interface IPairIteratorCallback<T> {
      /**
       * @param o1
       * @param o2
       */
      void pair(T o1, T o2);
    }
    
    /**
     * 
     * Iterates through each distinct unordered pair formed by the elements of a given iterator
     *
     * @param it
     * @param callback
     */
    public static <T> void iterateDistinctPairs(final Iterator<T> it, IPairIteratorCallback<T> callback) {
      List<T> list = Convert.toMinimumArrayList(new Iterable<T>() {
    
        @Override
        public Iterator<T> iterator() {
          return it;
        }
    
      });
    
      for (int outerIndex = 0; outerIndex < list.size() - 1; outerIndex++) {
        for (int innerIndex = outerIndex + 1; innerIndex < list.size(); innerIndex++) {
          callback.pair(list.get(outerIndex), list.get(innerIndex));
        }
      }
    }
    
    0 讨论(0)
  • 2020-11-21 06:47

    Just because this is what I got when I Googled this error, my problem was that I had

    if (value < other.value)
      return -1;
    else if (value >= other.value)
      return 1;
    else
      return 0;
    

    the value >= other.value should (obviously) actually be value > other.value so that you can actually return 0 with equal objects.

    0 讨论(0)
  • 2020-11-21 06:47

    Editing VM Configuration worked for me.

    -Djava.util.Arrays.useLegacyMergeSort=true
    
    0 讨论(0)
提交回复
热议问题