2017-03-04 4 views
1

私は、ファイル名のリストを持っているし、次の順序でこれらを比較したい:なぜこのcompareTo()メソッドはソート中に契約違反につながるのですか?

  • 「.rarを」で終わるすべての名前は「.r01" 、」.r02" と前にファイルを来る必要があります。 ...
  • 「.par2" で終わるすべての名前を私は私のJavaクラスのいずれかの次compareToの方法を使用していますので、

他の接尾辞で後のファイルが来る必要があります。

public class DownloadFile implements Comparable<DownloadFile> 
{ 
    // custom code ... 

    @Override 
    public int compareTo(DownloadFile other) 
    { 
     if(other == null) 
      throw new NullPointerException("Object other must not be null"); 

     // special cases -- .rar vs .par2 etc. 
     String thisStr = filename.toLowerCase(); 
     String oStr = other.getFilename().toLowerCase(); 
     if(thisStr.endsWith(".rar") && oStr.matches(".*\\.r[0-9]{2,}$")) 
      return -1; 
     if(thisStr.matches(".*\\.r[0-9]{2,}$") && oStr.endsWith(".rar")) 
      return 1; 
     if(!thisStr.endsWith(".par2") && oStr.endsWith(".par2")) 
      return -1; 
     if(thisStr.endsWith(".par2") && !oStr.endsWith(".par2")) 
      return 1; 

     // normal comparison based on filename strings 
     return thisStr.compareTo(oStr); 
    } 
} 

ただし、一部のデータに、これは、次のexecptionにつながる:

Exception in thread "Thread-12" java.lang.IllegalArgumentException: Comparison method violates its general contract! 

私は私がここで行方不明ですかを理解しようとしたが、私は、問題を見つけることができません。
契約に違反している場所を特定できますか?

PS:私が2番目の2つのifをコメントアウトすると、例外がスローされます。だから問題は最初の2つにあるifです。

+2

まず第一に、あなたは、このエラーの最も可能性の高い原因の一つを知っていますか? http://stackoverflow.com/a/8327575/1743880 – Tunaki

+0

".r02"エンディングのファイルは、最後に ".par"が付いたファイルの前に来る必要がありますか? –

+0

@GrzegorzGórkiewicz最後に ".par2"を付けた人の前にいるのですか?はい、それは正しいです。 – Matthias

答えて

5

推移的ではありません。
要素の線形順序付けはできません。

例による証明。私は小文字で線形順序や名前のため<を使用する単純化するために

c.par2 
b.notpar2 
a.par2 

あなたは小文字で名前を持つ3つのDownloadFile S(cba)を持っていると言います。

c.par2 < b.notpar2およびb.notpar2 < a.par2ですが、c.par2 < a.par2ではありません。
この関係はtransitiveではありません。ロジックで

...それは次のようになります:

cRbbRa、それはcRaことは事実ではありません。私はこのような何かのために行くだろう

あなたがしなければならないのは、直線的にあなたのファイルを注文する方法を答えることです...
:それは返したため、最後に

if(aMethodOnThis < aMethodOnOther) { 
    return -1; //or 1 
} 
if(aCompletelyDifferentCriterium) { 
    //... 
} 
return 0; //or return thisFileName.compareTo(otherFileName); 

return 0は、非常に重要です区別できないファイルの場合その場合

public class DownloadFile implements Comparable<DownloadFile>{ 

    String filename; 

    DownloadFile(String filename) { 
     this.filename = filename; 
    } 

    public String getFilename() { 
     return this.filename; 
    } 

    @Override 
    public String toString() { 
     return this.getFilename(); 
    } 

    @Override 
    public int compareTo(DownloadFile downloadFile) { 
     String thisStr = this.filename.toLowerCase(); 
     String oStr = downloadFile.getFilename().toLowerCase(); 
     if(thisStr.endsWith(".rar")) { 
      if(!oStr.endsWith(".rar")) 
       return -1; 
     } 
     if(oStr.endsWith(".rar")) { 
      if(!thisStr.endsWith(".rar")) 
       return 1; 
     } 
     if(thisStr.matches(".*\\.r[0-9]{2,}$")) { 
      if(!oStr.matches(".*\\.r[0-9]{2,}$")) 
       return -1; 
     } 
     if(oStr.matches(".*\\.r[0-9]{2,}$")) { 
      if(!thisStr.matches(".*\\.r[0-9]{2,}$")) 
       return 1; 
     } 
     if(thisStr.endsWith(".par2")) { 
      if(!oStr.endsWith(".par2")) 
       return -1; 
     } 
     if(oStr.endsWith(".par2")) { 
      if(!thisStr.endsWith(".par2")) 
       return 1; 
     } 
     return thisStr.compareTo(oStr); 
    } 

    public static void main(String[] args) { 
     List<DownloadFile> fileList = new ArrayList<>(); 
     fileList.add(new DownloadFile("a.rar")); 
     fileList.add(new DownloadFile("b.rar")); 
     fileList.add(new DownloadFile("a.r01")); 
     fileList.add(new DownloadFile("b.r01")); 
     fileList.add(new DownloadFile("a.r10")); 
     fileList.add(new DownloadFile("b.r10")); 
     fileList.add(new DownloadFile("a.par2")); 
     fileList.add(new DownloadFile("b.par2")); 
     fileList.add(new DownloadFile("a.other")); 
     fileList.add(new DownloadFile("b.other")); 
     Collections.shuffle(fileList); 
     Collections.sort(fileList); 
     System.out.println(fileList); 
    } 
} 

のJava 8からPredicate<String>それを短くするには便利です;)

@Override 
public int compareTo(DownloadFile downloadFile) { 
    String thisStr = this.filename.toLowerCase(); 
    String oStr = downloadFile.getFilename().toLowerCase(); 
    List<Predicate<String>> conditionList = new ArrayList<>(); 
    conditionList.add(s -> s.endsWith(".rar")); 
    conditionList.add(s -> s.matches(".*\\.r[0-9]{2,}$")); 
    conditionList.add(s -> s.endsWith(".par2")); 
    for(Predicate<String> condition : conditionList) { 
     int orderForCondition = 
       conditionHelper(thisStr, oStr, condition); 
     if(orderForCondition != 0) 
      return orderForCondition; 
    } 
    return thisStr.compareTo(oStr); 
} 

private int conditionHelper(String firstStr, String secondStr, 
          Predicate<String> condition) { 
    if(condition.test(firstStr)) 
     if(!condition.test(secondStr)) 
      return -1; 
    if(condition.test(secondStr)) 
     if(!condition.test(firstStr)) 
      return 1; 
    return 0; 
} 
+0

しかし、 'if(aMethodOnThis Matthias

+0

はい、そうです。しかし、あなたの条件はあまり変わらない。あなたの場合、それは決定に至ります... '.par2'エンディングのファイルが最初に他のファイルの前に来るようにしたいのですか?最後の4つの条件が重複していれば2つと思っています。 –

+0

私は元の質問に希望の注文ロジックの説明を追加しました。 – Matthias

関連する問題