簡體   English   中英

java.util.Date equals()似乎沒有按預期工作

[英]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)不應該equalscompareTo同意嗎? (我假設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.Datejava.sqljava.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.Datejava.util.TimestampgetTime()返回相同的值,所以你應該使用哪個實現無關緊要使用,關鍵將是相同的。 這種方式看似最簡單。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM