我很困惑:我写了一个比较日期的比较器。但是,当我运行 junit 测试时,它会返回不同的结果,具体取决于我是在我的 IDE 中还是在 maven 中运行它?!在我的 IDE 中它可以工作,而在 maven 中它失败了。在两个环境中使用相同的 1.8 jvm,但在具有 1.6 兼容模式的 maven 中。(请注意,这是一个遗留项目,使用 java.util.date 是不好的......(但这不是现在的重点)
以下是 IDE 的输出(正确):
Birthday [id=four, time=Thu Sep 23 20:54:24 CEST 2010]
Birthday [id=six, time=Wed Feb 01 01:01:01 CET 2012]
Birthday [id=five, time=Wed Feb 01 01:01:02 CET 2012]
Birthday [id=three, time=Tue Jan 08 17:30:43 CET 2019]
Birthday [id=one, ]
Birthday [id=two, ]
这里是 maven 的输出(不正确):
Birthday [id=four, time=Thu Sep 23 20:54:24 CEST 2010]
Birthday [id=three, time=Tue Jan 08 17:26:25 CET 2019]
Birthday [id=five, time=Wed Feb 01 01:01:02 CET 2012]
Birthday [id=six, time=Wed Feb 01 01:01:01 CET 2012]
Birthday [id=one, ]
Birthday [id=two, ]
这是代码(在 1.8 中运行它表示成功,在 1.6 中运行它表示失败):
import java.util.Date;
public class Birthday implements Comparable<Birthday>{
private String id;
private Date time;
public String getId() {
return this.id;
}
public Birthday(String id, Date time) {
this.id=id;
this.time=time;
}
public Date getTime() {
return this.time;
}
public void setTime(Date time) {
this.time = time;
}
@Override
public int compareTo(Birthday o) {
//if both are null return 0 for equals
if(this.time==null && o.getTime()==null) {
return 0;
}
//null birthdays should always be last
if(this.time==null) {
return 1;
}
if(o.getTime() == null) {
return -1;
}
return this.time.before(o.getTime()) ? 0 : 1;
}
@Override
public String toString() {
return this.id+" "+this.time;
}
}
@Test
public void testTime() {
Birthday info1 = new Birthday("one",null);
Birthday info2 = new Birthday("two", null);
Birthday info3 = new Birthday("three",new Date());
Birthday info4 = new Birthday("four",new Date(110,8,23,20,54,24));
Birthday info5 = new Birthday("five",new Date(112,1,1,1,1,2));
Birthday info6 = new Birthday("six",new Date(112,1,1,1,1,1));
ArrayList<Birthday> dates = new ArrayList<Birthday>();
dates.add(info1);
dates.add(info2);
dates.add(info4);
dates.add(info3);
dates.add(info5);
dates.add(info6);
Collections.sort(dates);
for(Birthday bs: dates) {
System.out.println(bs);
}
Assert.assertEquals(info4, dates.get(0));
Assert.assertEquals(info6, dates.get(1));
Assert.assertEquals(info7, dates.get(2));
Assert.assertEquals(info5, dates.get(3));
Assert.assertEquals(info3, dates.get(4));
Assert.assertEquals(info1, dates.get(5));
Assert.assertEquals(info2, dates.get(6));
}
这是怎么回事??
问题出在这一行:
return this.time.before(o.getTime()) ? 0 : 1;
如果此项目时间早于其他时间,则它们相等。这不是反身的,所以你打破了比较的要求。
不同的JVM 实现可以使用不同的排序算法,其中一个会表现出这个错误,而另一个不会
正如其他人指出的那样,问题出在compareTo
方法的最后一行。
由于Date
实现了Comparable
,最简单的解决方法是:
return this.time.compareTo(o.getTime());
你的compareTo()
方法有缺陷。 如果两个对象的时间都不null
,则归结
return this.time.before(o.getTime()) ? 0 : 1;
但如果o.time.before(this.getTime())
呢? 在这种情况下,您的compareTo()
必须可靠地返回 -1,但它返回 0。 java.util.Date
有一个自然的顺序,只要你首先依赖该类
return this.time.compareTo(o.getTime());
我发现了问题:比较器返回 0(表示相等),这是不正确的:
应该是 return this.time.before(o.getTime()) ? -1 : 1;
而不是 return this.time.before(o.getTime()) ? 0 : 1;
正如约翰所指出的更好:
return this.time.compareTo(o.getTime());
java 改变了 JDK 1.7 中的排序算法,该算法可能以不同的方式处理相等的值。所以这一切都在这个有趣的令人费解的案例中汇集在一起......