java.util.Date equals()似乎没有按预期工作

问题

我有一个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.我意识到我可以改变这个代码的结构,并且可能会使它工作.但这应该是有效的,我不想只是解决它,除非是一个已知的问题或事情.这似乎是错误

解决方法

正如 Mureinik所暗示的那样,Sotirios Delimanolis更具体地指出,这里的问题是与java.util.Date的实现.

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());
}

它需要一个额外的方法调用(和那种可笑的方法),但对我来说,涉及Map< Date,Foo>的代码的可读性增加了是值得的

相关文章

ArrayList简介:ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增...
一、进程与线程 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。 线程...
本文为博客园作者所写:&#160;一寸HUI,个人博客地址:https://www.cnblogs.com/zsql/ 简单的一个类...
#############java面向对象详解#############1、面向对象基本概念2、类与对象3、类和对象的定义格式4、...
一、什么是异常? 异常就是有异于常态,和正常情况不一样,有错误出错。在java中,阻止当前方法或作用域...
Collection接口 Collection接口 Collection接口 Collection是最基本的集合接口,一个Collection代表一组...