我有一个Map< Date,Foo>和一个来自数据库的对象列表,其中包含一个有效的属性,我想查看我的地图中的Date键是否等于数据库中的任何有效日期 – 如果是用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)不应该等于和比较同意? (我假设java.util.Date的实现至少应该尝试遵循java.lang.Comparable的建议).
2)The Date#equals
doc says this:
Thus,two Date objects are equal if and only if the getTime method returns the same long value for both.
…看起来getTime方法为这两个日期返回相同的long值,但相等则返回false.有什么想法可能会发生吗?我搜索过高低,但我还没有发现任何人描述同样的问题.
附:我被卡住了使用java.util.Date.请不要只推荐JodaTime.
P.P.S.我意识到我可以改变这个代码的结构,并且可能会使它工作.但这应该是有效的,我不想只是解决它,除非是一个已知的问题或事情.这似乎是错误的
解决方法
java.util.Date在java.sql包中扩展了3个类,所有这些都似乎做了类似的事情,在java中的区别并不清楚(似乎它们存在的原因只是使java类更精确地对齐sql数据类型) – 有关其差异的更多信息,请查看this very detailed answer.
现在,在看起来像是一个严重的设计缺陷,有人决定使equals()
asymmetric with java.sql.Timestamp
– 也就是说,即使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
uses parameter.equals(key)
in it’s implementation of containsKey()
(而不是key.equals(参数)),这个奇怪的结果出现在给定的情况下.
那么,如何解决呢?
1)在地图中使用长键,而不是一个Date(如Mureinik所说) – 由于java.util.Date和java.util.Timestamp从getTime()返回相同的值,所以你应该执行哪个实现使用,关键将是一样的.这样做似乎是最简单的.
2)在地图中使用之前对日期对象进行标准化.这样做需要更多的工作,但是对我来说似乎更可取,因为它更清楚地图是什么 – 一堆Foo每次存储在一段时间.这是我最后使用的方式,使用以下方法:
public Date getStandardizedDate(Date date) { return new Date(date.getTime()); }