Метод сравнения (compareTo) возвращает разные результаты в разных JVM
Я озадачен: я написал компаратор, который сравнивает даты. Однако, когда я запускаю тест junit, он возвращает разные результаты в зависимости от того, запускаю ли я его в своей IDE или в maven?! В моей IDE это работает, а в Maven это не удается. Использование одного и того же 1.8 jvm в обеих средах, однако в maven с режимом совместимости 1.6. (Обратите внимание, что это устаревший проект, и использование 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 ответа
Как уже отмечалось, проблема заключается в последней строке compareTo
метод.
поскольку Date
инвентарь Comparable
самое простое решение:
return this.time.compareTo(o.getTime());
Проблема в этой строке:
return this.time.before(o.getTime()) ? 0 : 1;
если это время элементов раньше времени других, они равны. Это не рефлексивно, поэтому вы нарушаете требования для сравнения.
Разные реализации JVM могут использовать разные алгоритмы сортировки, где одна будет демонстрировать эту ошибку, а другая нет
Я думаю, что нашел проблему: компаратор возвратил 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, который, вероятно, по-разному обрабатывает одинаковые значения. Так что все собрались вместе в этом интересном загадочном деле...
Ваш 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());