2013-07-15 84 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完成這項工作。

數字比較兩個Double對象。

有兩種方法,其中通過該方法執行的比較從那些由Java語言數值比較運算符執行不同(<,< =,==,> =,>)當施加到基本的double值:

  1. 該方法認爲Double.NaN等於其自身並且大於所有其他double值(包括Double.POSITIVE_INFINITY)。

  2. 該方法認爲0.0d大於-0.0d。

這確保了由此方法施加的Double對象的自然順序與equals一致。

我相信你得到這個異常,因爲你現在的執行情況可能並不容易對付Double.NaNpositive/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()

注意文檔該類在大多數情況下,兩個實例雙,D1和D2,d1.equals的價值(d2)當且僅當 d1.doubleValue()== d2.doubleValue()

也具有值true。但是,有兩個例外:

如果d1和d2都表示Double。NaN,那麼即使Double.NaN == Double.NaN的值爲false,equals方法也會返回true。 如果d1表示+0.0,而d2表示-0.0,反之亦然,即使+0.0 == - 0.0的值爲true,相等測試的值爲false。

+0

嘿那裏 - 謝謝你的答案。我有一種感覺,你是對的,但getDist()應該永遠是NAN還是無窮的真的沒有理由。 getDist的值是在排序調用之前爲集合中的每個項目設置的。我會按照您的建議嘗試更改比較方法,但不幸的是,由於我無法在本地重現此錯誤,因此我必須將其發佈到Play商店並查看錯誤報告是否停止。一旦我知道它是否有效,我會接受這個答案。謝謝! – DiscDev

+0

此外,我完全同意 - 沒有使用重新發明輪子 - 我想我只是習慣於不得不爲自定義對象編寫我自己的比較器,我忘記了已經存在用於內置對象庫的比較器。有時候,我會在自己的代碼中太深刻,以便記住像這樣的東西:) – DiscDev

+1

請注意,使用數字比較運算符時,'POSITIVE_INFINITY'和'NEGATIVE_INFINITY'不應該是問題。這只是'NaN'和正面/負面的零案件,這將是有問題的。 –

2

不是比較兩個Double對象,而應該真的比較它們的getDoubleValue())。比較兩個對象並不一定意味着它們的值是相等的。

+0

亞歷克斯 - 我認爲java會足夠聰明來比較double值...這種比較方法在較老的Android設備上工作良好,因此不可能比較對象引用,否則我從比較中得到的結果將不穩定。我認爲Ken Wolf和The New Idiot正在做些什麼... – DiscDev

+0

@ spotdog13 <運算符將取消Double對象的比較,但==運算符將比較對象引用。應始終將對象與.equals()進行比較。 –

+0

啊,是的,邁克爾你是對的。我知道<將解除它們......實際上,getDist()實際上不太可能是2個對象相同的,因爲這個值涉及精度......這可能會解釋爲什麼這樣排序正確,我錯過了我的錯字:)。我實際上並沒有意識到getDist()是一個Double(認爲它是一個double,但它是一個從DB中抽取的值,我相信我需要在創建表時選擇Double),直到我問這個問題。 – DiscDev

相關問題