gpt4 book ai didi

java - 为什么这个 compareTo() 方法会导致排序时违反契约?

转载 作者:行者123 更新时间:2023-11-29 07:31:02 24 4
gpt4 key购买 nike

我有一个文件名列表,想按以下顺序比较它们:

  • 所有以“.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);
}
}

但是,在某些数据上这会导致以下异常:

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

我试图了解我在这里遗漏了什么,但找不到问题所在。
你能看出我哪里违反了契约(Contract)吗?

PS:如果我注释掉后两个 if,那么异常仍然会被抛出。所以问题出在前两个 if 上。

最佳答案

它是不可传递的。
元素的线性排序是不可能的。

反例证明。

假设你有 3 DownloadFile s ( c , b , a ) 小写名称:

c.par2
b.notpar2
a.par2

为了简化,我将使用 <用于线性排序和小写名称。

c.par2 < b.notpar2b.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);
}
}

让它更短Predicate<String>来自 Java 8 的版本派上用场了 ;)

@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;
}

关于java - 为什么这个 compareTo() 方法会导致排序时违反契约?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42602096/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com