当前位置: 首页 > 面试题库 >

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

充昌勋
2023-03-14
问题内容

问题

我有一个Map<Date, Foo>,以及一个带有effectiveDate属性的数据库对象列表,我想检查一下Date我的映射中的键是否等于effectiveDate数据库中的任何s-
如果是,请用填充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医生说这个:

因此,当且仅当getTime方法返回两个相同的long值时,两个Date对象才相等。

…看起来该getTime方法对于这两个日期都返回相同的long值,但equal返回false。任何想法为什么会发生这种情况?我搜索过很多,但没有找到任何描述相同问题的人。

PS我被困住了java.util.Date。请不要只是推荐JodaTime。

PPS我意识到我可以更改此代码的结构并可能使其工作。但这应该可以工作,除非有已知问题或其他原因,否则我不想仅仅解决它。似乎是 错误的


问题答案:

正如Mureinik所暗示和SotiriosDelimanolis更加具体指出的那样,这里的问题在于实现java.util.Date

java.util.Datejava.sql包中被3个类扩展,所有这些类似乎都做类似的事情,并且它们在java中的区别根本不清楚(似乎它们存在的原因仅仅是使Java类更准确地与SQL数据类型对齐)-有关它们之间差异的更多信息,请查看此非常详细的答案。

现在,在看似严重的设计缺陷中,有人决定对它进行equals()不对称处理java.sql.Timestamp
-也就是说,timestamp.equals(date)即使date.equals(timestamp)返回true ,也可能返回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使用parameter.equals(key)了该containsKey()key.equals(parameter)结果,因此在给定的情况下会显示出一个奇怪的结果。

那么,如何解决这个问题?

1)使用Long的地图键,而不是一个Date(如Mureinik说明)
-自从java.util.Datejava.util.Timestamp来自返回相同的值getTime(),它不应该不管你使用的实施,关键将是相同的。这种方法看起来确实是最简单的。

2)在地图中使用日期对象之前,先对其进行标准化。这种方法需要一点点工作,但对我来说似乎更可取,因为它更清楚地图是什么-
一堆Foo存储在一个时刻。这是我最终通过以下方法使用的方法:

public Date getStandardizedDate(Date date) {
  return new Date(date.getTime());
}

这需要一个额外的方法调用(这在当时是一种荒谬的调用),但是对我而言,涉及的代码的更高可读性Map<Date,Foo>是值得的。



 类似资料:
  • 我正在使用spring Roo并希望访问Controller类中的一个bean,该类在ApplicationContext.xml中具有以下配置: 配置类本身是: 在我的Controller中,我认为一个简单的Autowired注释应该可以完成这项工作 在启动过程中,spring在setSkipWeeks方法中打印消息。不幸的是,每当我在控制器中调用config.getSkipWeeks()时,它

  • 当我运行以下程序时,它只打印 然而,从Java 8的equalsIgnoreCase文档中我们发现: 如果以下至少一项为真,则两个字符c1和c2被视为相同的忽略情况: •对每个字符应用java.lang.character.ToUpperCase(char)方法会产生相同的结果 所以我的问题是为什么这个程序不打印 在这两种操作中,都使用了大写字符。

  • 我试图使用来传输我根据前面的问题设置的自定义标头。 我在文件中读到... 我的属性包括:

  • 我正在和selenium一起工作,刮一些数据。 有一个按钮在页面上,我正在点击说“Custom_Cols”。这个按钮为我打开了一个窗口,我可以在那里选择我的列。 我的问题是为什么新窗口上的元素不可见,即使我正在等待元素的可见。补充一下,我已经尝试增加延迟时间,但我还是会偶尔出现这个错误。 我的密码在这里

  • 我正在使用Grails 2.0.1中的springsecurity插件。我的角色层次结构和其他s2属性如下所示。