私はこれについて多くの質問を見て、問題を解決しようとしましたが、1時間のグーグルと多くの試行錯誤の後、私はまだそれを修正できません。皆さんの何人かが問題をキャッチすることを願っています。
これは私が得るものです:
Java.lang.IllegalArgumentException: Comparison method violates its general contract!
at Java.util.ComparableTimSort.mergeHi(ComparableTimSort.Java:835)
at Java.util.ComparableTimSort.mergeAt(ComparableTimSort.Java:453)
at Java.util.ComparableTimSort.mergeForceCollapse(ComparableTimSort.Java:392)
at Java.util.ComparableTimSort.sort(ComparableTimSort.Java:191)
at Java.util.ComparableTimSort.sort(ComparableTimSort.Java:146)
at Java.util.Arrays.sort(Arrays.Java:472)
at Java.util.Collections.sort(Collections.Java:155)
...
そして、これは私のコンパレータです:
@Override
public int compareTo(Object o) {
if(this == o){
return 0;
}
CollectionItem item = (CollectionItem) o;
Card card1 = CardCache.getInstance().getCard(cardId);
Card card2 = CardCache.getInstance().getCard(item.getCardId());
if (card1.getSet() < card2.getSet()) {
return -1;
} else {
if (card1.getSet() == card2.getSet()) {
if (card1.getRarity() < card2.getRarity()) {
return 1;
} else {
if (card1.getId() == card2.getId()) {
if (cardType > item.getCardType()) {
return 1;
} else {
if (cardType == item.getCardType()) {
return 0;
}
return -1;
}
}
return -1;
}
}
return 1;
}
}
何か案が?
例外メッセージは実際にはかなり説明的です。それが言及している契約は、推移性:if A > B
and B > C
then for A
、B
and C
:A > C
。紙と鉛筆で確認しましたが、コードにはいくつかの穴が開いているようです:
if (card1.getRarity() < card2.getRarity()) {
return 1;
card1.getRarity() > card2.getRarity()
の場合、-1
を返しません。
if (card1.getId() == card2.getId()) {
//...
}
return -1;
IDが等しくない場合、-1
を返します。どのIDが大きいかに応じて、-1
または1
を返す必要があります。
これを見てください。はるかに読みやすいだけでなく、実際に機能するはずです。
if (card1.getSet() > card2.getSet()) {
return 1;
}
if (card1.getSet() < card2.getSet()) {
return -1;
};
if (card1.getRarity() < card2.getRarity()) {
return 1;
}
if (card1.getRarity() > card2.getRarity()) {
return -1;
}
if (card1.getId() > card2.getId()) {
return 1;
}
if (card1.getId() < card2.getId()) {
return -1;
}
return cardType - item.getCardType(); //watch out for overflow!
次のクラスを使用して、コンパレータの推移性バグを特定できます。
/**
* @author Gili Tzabari
*/
public final class Comparators
{
/**
* Verify that a comparator is transitive.
*
* @param <T> the type being compared
* @param comparator the comparator to test
* @param elements the elements to test against
* @throws AssertionError if the comparator is not transitive
*/
public static <T> void verifyTransitivity(Comparator<T> comparator, Collection<T> elements)
{
for (T first: elements)
{
for (T second: elements)
{
int result1 = comparator.compare(first, second);
int result2 = comparator.compare(second, first);
if (result1 != -result2)
{
// Uncomment the following line to step through the failed case
//comparator.compare(first, second);
throw new AssertionError("compare(" + first + ", " + second + ") == " + result1 +
" but swapping the parameters returns " + result2);
}
}
}
for (T first: elements)
{
for (T second: elements)
{
int firstGreaterThanSecond = comparator.compare(first, second);
if (firstGreaterThanSecond <= 0)
continue;
for (T third: elements)
{
int secondGreaterThanThird = comparator.compare(second, third);
if (secondGreaterThanThird <= 0)
continue;
int firstGreaterThanThird = comparator.compare(first, third);
if (firstGreaterThanThird <= 0)
{
// Uncomment the following line to step through the failed case
//comparator.compare(first, third);
throw new AssertionError("compare(" + first + ", " + second + ") > 0, " +
"compare(" + second + ", " + third + ") > 0, but compare(" + first + ", " + third + ") == " +
firstGreaterThanThird);
}
}
}
}
}
/**
* Prevent construction.
*/
private Comparators()
{
}
}
失敗したコードの前でComparators.verifyTransitivity(myComparator, myCollection)
を呼び出すだけです。
また、JDKのバージョンと関係があります。 JDK6でうまく機能する場合は、JDK 7の実装方法が変更されているため、JDK 7で問題が発生する可能性があります。
これを見てください:
説明:Java.util.Arrays.sort
および(間接的に)Java.util.Collections.sort
で使用されるソートアルゴリズムが置き換えられました。新しいソート実装は、IllegalArgumentException
コントラクトに違反するComparable
を検出した場合、Comparable
をスローする場合があります。以前の実装では、このような状況を静かに無視していました。以前の動作が必要な場合は、新しいシステムプロパティJava.util.Arrays.useLegacyMergeSort
を使用して、以前のmergesortの動作を復元できます。
正確な理由はわかりません。ただし、sortを使用する前にコードを追加した場合。大丈夫でしょう。
System.setProperty("Java.util.Arrays.useLegacyMergeSort", "true");
次の場合を考えてください:
まず、o1.compareTo(o2)
が呼び出されます。 card1.getSet() == card2.getSet()
はたまたまcard1.getRarity() < card2.getRarity()
であるため、1を返します。
次に、o2.compareTo(o1)
が呼び出されます。繰り返しますが、card1.getSet() == card2.getSet()
はtrueです。次に、次のelse
にスキップすると、card1.getId() == card2.getId()
がtrueになり、cardType > item.getCardType()
もtrueになります。再び1を返します。
それから、o1 > o2
、およびo2 > o1
。契約を破った。
if (card1.getRarity() < card2.getRarity()) {
return 1;
ただし、card2.getRarity()
がcard1.getRarity()
より小さい場合、-1を返さない場合があります。
同様に他のケースを見逃しています。私はこれを行うでしょう、あなたはあなたの意図に応じて変更することができます:
public int compareTo(Object o) {
if(this == o){
return 0;
}
CollectionItem item = (CollectionItem) o;
Card card1 = CardCache.getInstance().getCard(cardId);
Card card2 = CardCache.getInstance().getCard(item.getCardId());
int comp=card1.getSet() - card2.getSet();
if (comp!=0){
return comp;
}
comp=card1.getRarity() - card2.getRarity();
if (comp!=0){
return comp;
}
comp=card1.getSet() - card2.getSet();
if (comp!=0){
return comp;
}
comp=card1.getId() - card2.getId();
if (comp!=0){
return comp;
}
comp=card1.getCardType() - card2.getCardType();
return comp;
}
}
私はいくつかの基準(日付、および同じ日付の場合、他のもの...)でソートする必要がありました。古いバージョンのJavaでEclipseで機能していたものがAndroidで機能しなくなりました:比較方法が契約に違反しています...
StackOverflowを読んだ後、日付が同じ場合にcompare()から呼び出す別の関数を作成しました。この関数は、基準に従って優先順位を計算し、compare()に-1、0、または1を返します。今は機能しているようです。
次のStockPickBean
のようなクラスでも同じエラーが発生しました。このコードから呼び出されます:
List<StockPickBean> beansListcatMap.getValue();
beansList.sort(StockPickBean.Comparators.VALUE);
public class StockPickBean implements Comparable<StockPickBean> {
private double value;
public double getValue() { return value; }
public void setValue(double value) { this.value = value; }
@Override
public int compareTo(StockPickBean view) {
return Comparators.VALUE.compare(this,view); //return
Comparators.SYMBOL.compare(this,view);
}
public static class Comparators {
public static Comparator<StockPickBean> VALUE = (val1, val2) ->
(int)
(val1.value - val2.value);
}
}
同じエラーが発生した後:
Java.lang.IllegalArgumentException:比較メソッドが一般契約に違反しています!
私はこの行を変更しました:
public static Comparator<StockPickBean> VALUE = (val1, val2) -> (int)
(val1.value - val2.value);
に:
public static Comparator<StockPickBean> VALUE = (StockPickBean spb1,
StockPickBean spb2) -> Double.compare(spb2.value,spb1.value);
これでエラーが修正されます。