[英]java.util.Date equals() doesn't seem to work as expected
我有一個Map<Date, Foo>
,以及一個帶有effectiveDate
屬性的數據庫中的對象列表,我想查看我的map中的Date
鍵是否等於數據庫中的任何effectiveDate
- 如果所以,用Foo
做點什么。
代碼看起來像這樣:
for (Bar bar : databaseBars) {
Foo foo = new Foo();
if (dateMap.containsKey(bar.getEffectiveDate()) {
foo = dateMap.get(bar.getEffectiveDate());
}
// do stuff with foo and bar
}
但是, dateMap.containsKey
調用總是返回false,即使我確定它有時也存在。
作為一個完整性檢查,我打印出日期的長值,以及equals()
調用和compareTo()
調用的結果:
for (Date keyDate : dateMap.keySet()) {
if (keyDate == null) {
continue; // make things simpler for now
}
Date effDate = bar.getEffectiveDate();
String template = "keyDate: %d; effDate: %d; equals: %b; compareTo: %d\n";
System.out.printf(template, keyDate.getTime(), effDate.getTime(), effDate.equals(keyDate), effDate.compareTo(keyDate));
}
結果:
keyDate: 1388534400000; effDate: 1388534400000; equals: false; compareTo: 0
keyDate: 1420070400000; effDate: 1388534400000; equals: false; compareTo: -1
keyDate: 1388534400000; effDate: 1420070400000; equals: false; compareTo: 1
keyDate: 1420070400000; effDate: 1420070400000; equals: false; compareTo: 0
keyDate: 1388534400000; effDate: 1388534400000; equals: false; compareTo: 0
keyDate: 1420070400000; effDate: 1388534400000; equals: false; compareTo: -1
keyDate: 1388534400000; effDate: 1420070400000; equals: false; compareTo: 1
keyDate: 1420070400000; effDate: 1420070400000; equals: false; compareTo: 0
keyDate: 1388534400000; effDate: 1388534400000; equals: false; compareTo: 0
keyDate: 1420070400000; effDate: 1388534400000; equals: false; compareTo: -1
keyDate: 1388534400000; effDate: 1420070400000; equals: false; compareTo: 1
keyDate: 1420070400000; effDate: 1420070400000; equals: false; compareTo: 0
1)不應該equals
和compareTo
同意嗎? (我假設java.util.Date
的實現至少應該嘗試遵循java.lang.Comparable
的建議)。
2) Date#equals
doc說 :
因此,當且僅當getTime方法為兩者返回相同的long值時,兩個Date對象才相等。
...看起來getTime
方法為這兩個日期返回相同的long值,但是equal
返回false。 任何想法為什么會這樣? 我搜索過高低,但我沒有發現任何人描述同樣的問題。
PS我被困在使用java.util.Date
。 請不要只推薦JodaTime。
PPS我意識到我可以改變這段代碼的結構,並可能讓它運行起來。 但這應該有效,我不想只是解決它,除非它是一個已知的問題或什么的。 這似乎不對 。
正如Mureinik暗示和Sotirios Delimanolis更具體地指出的那樣,問題在於java.util.Date
的實現。
java.util.Date
在java.sql
包java.util.Date
擴展了3個類,所有這些類似乎都做了類似的事情,並且它們在java中的區別並不清楚(似乎它們存在的原因只是使java類成為更准確地對齊SQL數據類型) - 有關它們差異的更多信息,請查看這個非常詳細的答案 。
現在,在一個看似嚴重的設計缺陷中,有人決定使用java.sql.Timestamp
使equals()
不對稱 - 也就是說,即使date.equals(timestamp)
返回true, timestamp.equals(date)
也可能返回false。 很好的主意。
我寫了幾行來看看哪些java.sql
類演示了這個荒謬的屬性 - 顯然它只是Timestamp
。 這段代碼:
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
System.out.println("sqlDate equals utilDate:\t" + sqlDate.equals(utilDate));
System.out.println("utilDate equals sqlDate:\t" + utilDate.equals(sqlDate));
java.sql.Time time = new java.sql.Time(utilDate.getTime());
System.out.println("time equals utilDate:\t\t" + time.equals(utilDate));
System.out.println("utilDate equals time:\t\t" + utilDate.equals(time));
java.sql.Timestamp timestamp = new java.sql.Timestamp(utilDate.getTime());
System.out.println("timestamp equals utilDate:\t" + timestamp.equals(utilDate));
System.out.println("utilDate equals timestamp:\t" + utilDate.equals(timestamp));
收益率:
sqlDate equals utilDate: true
utilDate equals sqlDate: true
time equals utilDate: true
utilDate equals time: true
timestamp equals utilDate: false
utilDate equals timestamp: true
由於java.util.HashMap
在其執行containsKey()
(而不是key.equals(parameter)
)時使用了parameter.equals(key)
,因此在給定的情況下會出現這個奇怪的結果。
那么,如何解決這個問題呢?
1)在地圖中使用Long
鍵而不是Date
(如Mureinik所說) - 因為java.util.Date
和java.util.Timestamp
從getTime()
返回相同的值,所以你應該使用哪個實現無關緊要使用,關鍵將是相同的。 這種方式看似最簡單。
2)在地圖中使用之前標准化日期對象。 這種方式需要更多的工作,但對我來說似乎更令人滿意,因為它更清楚地圖是什么 - 一堆Foo
每一個都存儲在一個時刻。 這是我最終使用的方式,使用以下方法:
public Date getStandardizedDate(Date date) {
return new Date(date.getTime());
}
它需要一個額外的方法調用(並且有點像一個荒謬的方法),但對我來說,涉及Map<Date, Foo>
的代碼的可讀性增加是值得的。
第1部分:“不應該equals
compareTo
?*
沒有; compareTo應該同意equals,但反過來是無關緊要的。
compareTo是關於排序順序。 等於是平等。 考慮賽車,可以在實踐中按最快單圈時間排序以確定起始位置。 平均單圈時間並不意味着它們是同一輛車。
第2部分:平等日期。
數據庫調用將返回一個java.sql.Date,雖然它可以賦值給java.util.Dare,因為它擴展了它,不會相等,因為類是不同的。
解決方法可能是:
java.util.Date test;
java.sql.Date date;
if (date.equals(new java.sql.Date(test.getTime()))
從數據庫返回的Date
對象可能是java.sql.Timestamp
,它不能等於java.util.Date
對象。 我只需從getTime()
返回long,並將其用作HashMap
的鍵。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.