比较方法(compareTo)在不同的JVM中返回不同的结果

时间:2019-01-08 17:00:48

标签: java sorting comparator comparable compareto

我很困惑:我写了一个比较器来比较日期。但是,当我运行junit test时,它会返回不同的结果,具体取决于是否在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));
}

发生了什么事?

4 个答案:

答案 0 :(得分:3)

问题出在这一行:

return this.time.before(o.getTime()) ? 0 : 1;

如果该项目时间早于其他时间,则它们相等。这不是自反的,因此您违反了compareTo的要求。

不同的JVM实现可以使用不同的排序算法,其中一种会出现此错误,而另一种则不会

答案 1 :(得分:3)

正如其他人指出的那样,问题出在compareTo方法的最后一行。

由于Date实现了Comparable,所以最简单的解决方法是:

return this.time.compareTo(o.getTime());

答案 2 :(得分:1)

您的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());

答案 3 :(得分:1)

我认为我发现了问题: 比较器返回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中的sort algorithm,它可能以不同的方式处理相等的值。因此,在这个有趣的令人困惑的案例中,所有这些都融合在一起了。

相关问题