2013-07-15 2 views
2

Я видел другие вопросы об этом исключении, но мой метод сравнения настолько прост, что я не могу понять, что с ним не так, и я не могу воспроизвести его ни с одним из Устройства Android, которыми я владею.Android - метод сравнения нарушает его общий контракт

Я получаю это исключение от некоторых пользователей моего приложения для Android, большинство из которых, похоже, находятся на очень новых устройствах, таких как GS3 или GS4, которые, как я предполагаю, запускают вариант слияния Java 7 слияния.

Вот мой метод сравнения:

  Collections.sort(collectionOfThings, new Comparator<Thing>() 
      { 
       public int compare(Thing lhs, Thing rhs) 
       { 
        //getDist() returns a Double with a capital D...perhaps that has something to do with it? 
        if(lhs.getDist() < rhs.getDist()) 
        { 
         return -1; 
        } 
        if(lhs.getDist() == rhs.getDist()) 
        { 
         return 0; 
        } 

        return 1; 
       }; 
      }); 

Вот исключение:

Caused by: java.lang.IllegalArgumentException: Comparison method violates its general contract! 
    at java.util.TimSort.mergeLo(TimSort.java:743) 
    at java.util.TimSort.mergeAt(TimSort.java:479) 
    at java.util.TimSort.mergeCollapse(TimSort.java:404) 
    at java.util.TimSort.sort(TimSort.java:210) 
    at java.util.TimSort.sort(TimSort.java:169) 
    at java.util.Arrays.sort(Arrays.java:2038) 
    at java.util.Collections.sort(Collections.java:1891) 

Кажется, ограничивается Android 4.0+. Любая помощь приветствуется.

+2

Я не уверен, что это исправит его, но я просто сделаю 'return lhs.getDist(). CompareTo (rhs.getDist());' http://docs.oracle.com/javase/6/ docs/api/java/lang/Double.html # compareTo (java.lang.Double) –

+1

http://stackoverflow.com/questions/8327514/comparison-method-violates-its-general-contract –

+0

Есть ли вероятность, что 'Thing.getDist()' изменяет 'Thing'? –

ответ

6

Не использовать в изобретении колеса. Я считаю, что вы должны просто вернуть lhs.getDist().compareTo(rhs.getDist()); и предоставить предоставленную реализацию compareTo выполнить эту работу.

Сравнивает два двойных объекта с численностью.

Есть два способа, в которых сравнение, выполненное этим способом, отличаются от тех, осуществляется с помощью языка Java числовых операторов сравнения (<, < =, ==,> =,>) при нанесении на примитивные двойные значения:

  1. Double.NaN считается, что этот метод равен самому себе и больше всех других двойных значений (включая Double.POSITIVE_INFINITY).

  2. 0.0d считается, что этот метод должен быть больше -0.0d.

Это гарантирует, что естественный порядок объектов Double, наложенных этим методом, согласуется с равными.

Я считаю, что вы получите это исключение, потому что ваша существующая реализация не может быть склонны иметь дело с Double.NaN и positive/negative zero ценности, и все же честь генподряд. Посмотрите на OpenJDK Double#compare(double,double) исходного кода:

public static int More ...compare(double d1, double d2) { 
    if (d1 < d2) 
     return -1;   // Neither val is NaN, thisVal is smaller 
    if (d1 > d2) 
     return 1;   // Neither val is NaN, thisVal is larger 

    long thisBits = Double.doubleToLongBits(d1); 
    long anotherBits = Double.doubleToLongBits(d2); 

    return (thisBits == anotherBits ? 0 : // Values are equal 
      (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) 
      1));       // (0.0, -0.0) or (NaN, !NaN) 
} 

идет также через документацию Double#equals()

Обратите внимание, что в большинстве случаев, два экземпляра класса Double, d1 и d2, значения d1.equals (d2) истинна тогда и только тогда, когда d1.doubleValue() == d2.doubleValue()

также имеет значение true. Однако есть два исключения:

Если d1 и d2 оба представляют собой Double.NaN, то метод equals возвращает true, хотя Double.NaN == Double.NaN имеет значение false. Если d1 представляет +0.0, тогда как d2 представляет -0.0, или наоборот, равный тест имеет значение false, хотя значение +0.0 == - 0.0 имеет значение true.

+0

Эй, спасибо за ответ. У меня такое чувство, что вы правы, но нет никакой причины, почему getDist() должен быть NAN или бесконечностью. Значение getDist устанавливается для каждого элемента в коллекции непосредственно перед вызовом сортировки. Я попытаюсь изменить метод сравнения, как вы предлагали, но, к сожалению, поскольку я не могу воспроизвести эту ошибку локально, я должен выпустить ее в Play Store и посмотреть, прекращаются ли отчеты об ошибках. Я отвечу на этот ответ, когда узнаю, сработало ли это. Благодаря! – DiscDev

+0

Кроме того, я полностью согласен - нет смысла изобретать колесо - я полагаю, что я просто привык к тому, чтобы писать свои собственные компараторы для пользовательских объектов, которые, как я забыл, уже существуют компараторы для встроенной библиотеки объектов. Иногда я слишком глубоко в своем собственном коде, чтобы запомнить такие вещи :) – DiscDev

+1

Обратите внимание, что 'POSITIVE_INFINITY' и' NEGATIVE_INFINITY' не должны быть проблемой при использовании операторов численного сравнения. Это просто «NaN» и положительные/отрицательные нулевые случаи, которые были бы проблематичными. –

2

Вместо сравнения двух объектов Double, вы действительно должны сравнивать их значения (getDoubleValue()). Сравнение двух объектов не обязательно означает, что их значения равны.

+0

Alex - Я думал, что java будет достаточно умным, чтобы сравнить двойные значения ... этот метод сравнения отлично работает на старых Android-устройствах, поэтому нет никакой возможности, что он сравнивает ссылки на объекты, в противном случае результаты, которые я получаю от сравнения, будут неустойчивыми. Я думаю, что Кен Вольф и The New Idiot находятся на чем-то ... – DiscDev

+0

@ spotdog13 <оператор будет удалять объекты Double для сравнения, но оператор == будет сравнивать ссылки на объекты. Объекты всегда должны сравниваться с .equals(). –

+0

Ах да, Майкл, вы правы. Я знал, что <будет распаковывать их ... на самом деле очень маловероятно, что getDist() будет когда-либо одинаковым для двух объектов из-за точности, связанной с значением ... что, вероятно, объяснит, почему это правильно сортирует, и я пропустил свой опечатка :). Я действительно не понимал, что getDist() был двойным (считалось, что это двойной, но это значение, извлеченное из БД, которое, как мне кажется, потребовало от меня выбора Double в момент создания таблицы), пока я не задал этот вопрос. – DiscDev

Смежные вопросы