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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27661640/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 12:12:10  来源:igfitidea点击:

java.util.Date equals() doesn't seem to work as expected

javadate

提问by ethanbustad

Problem

问题

I have a Map<Date, Foo>, and a list of objects from the database with an effectiveDateproperty, and I want to check to see if the Datekeys in my map are equal to any of the effectiveDates in the database - if so, do stuff with Foo.

我有一个Map<Date, Foo>, 和一个来自数据库的具有effectiveDate属性的对象列表,我想检查Date我的地图中的键是否等于effectiveDate数据库中的任何s - 如果是,请使用Foo.

The code looks something like this:

代码如下所示:

for (Bar bar : databaseBars) {
  Foo foo = new Foo();
  if (dateMap.containsKey(bar.getEffectiveDate()) {
    foo = dateMap.get(bar.getEffectiveDate());
  }
  // do stuff with foo and bar
}

However, the dateMap.containsKeycall always returns false, even though I'm sure it's sometimes there.

然而,dateMap.containsKey调用总是返回 false,即使我确定它有时在那里。

Investigation

调查

As a sanity check, I've printed out the long values of the dates, as well as the results of an equals()call and a compareTo()call:

作为健全性检查,我打印了日期的长值,以及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));
}

The results:

结果:

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

Question

问题

1) Shouldn't equalsand compareToagree? (I assume the implementation of java.util.Dateat least should try to follow the recommendation of java.lang.Comparable).

1) 不应该equals并且compareTo同意?(我假设执行java.util.Date至少应该尽量遵循 的建议java.lang.Comparable)。

2) The Date#equalsdoc says this:

2)Date#equals医生说这个

Thus, two Date objects are equal if and only if the getTime method returns the same long value for both.

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

...Looks like the getTimemethod returns the same long value for both of these dates, yet equalreturns false. Any ideas why this might be happening? I've searched high and low, but I haven't found anyone describing the same problem.

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

P.S. I'm stuck using java.util.Date. Please don't just recommend JodaTime.

PS我被困在使用java.util.Date. 请不要只推荐 JodaTime。

P.P.S. I realize I could just change the structure of this code and probably get it working. But this should work, and I don't want to just work around it, unless it's a known issue or something. It just seems wrong.

PPS 我意识到我可以更改此代码的结构并可能使其正常工作。但这应该可行,我不想只是解决它,除非这是一个已知问题或其他问题。只是好像不对

采纳答案by ethanbustad

As Mureinikhinted at and Sotirios Delimanolispointed out more specifically, the problem here is with the implementation of java.util.Date.

正如Mureinik 所暗示的和Sotirios Delimanolis更具体地指出的那样,这里的问题在于java.util.Date.

java.util.Dateis extended by 3 classes in the java.sqlpackage, all of which seem to do similar things and whose distinction in java is not at all clear (seems like the reason for their existence is simply to make java classes which align more accurately to SQL datatypes) - for more information on their differences, check out this very detailed answer.

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

Now, in what seems like a serious design flaw, someone decided to make equals()asymmetric with java.sql.Timestamp- that is, timestamp.equals(date)could return false even if date.equals(timestamp)returns true. Great idea.

现在,在一个看似严重的设计缺陷中,有人决定使用equals()不对称java.sql.Timestamp- 也就是说,timestamp.equals(date)即使date.equals(timestamp)返回 true ,也可能返回 false 。好点子。

I wrote a few lines to see which java.sqlclasses demonstrate this ridiculous property - apparently it's just Timestamp. This code:

我写了几行来看看哪些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));

Yields this:

产生这个:

sqlDate equals utilDate:    true
utilDate equals sqlDate:    true
time equals utilDate:       true
utilDate equals time:       true
timestamp equals utilDate:  false
utilDate equals timestamp:  true

Since java.util.HashMapuses parameter.equals(key)in it's implementation of containsKey()(rather than key.equals(parameter)), this one strange result shows up in the given situation.

由于java.util.HashMap用于parameter.equals(key)containsKey()(而不是key.equals(parameter))的实现,这个奇怪的结果出现在给定的情况下。

So, how to get around this?

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

1) Use a Longkey in the map rather than a Date(as Mureinik noted) - since java.util.Dateand java.util.Timestampreturn the same value from getTime(), it shouldn't matter which implementation you're using, the key will be the same. This way does seem like the simplest.

1)Long在地图中使用一个键而不是一个Date(如 Mureinik 所指出的) - 因为java.util.Datejava.util.Timestamp从 返回相同的值getTime(),所以无论您使用哪种实现,键都是相同的。这种方式似乎是最简单的。

2) Standardize the date object before using it in the map. This way requires a tiny bit more work, but to me seems more desirable as it's more clear what the map is - a bunch of Fooeach stored against a moment in time. This is the way I ended up using, with the following method:

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

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

It takes an extra method call (and kind of a ridiculous one at that), but to me the increased readability of the code involving the Map<Date, Foo>is worth it.

它需要一个额外的方法调用(这有点荒谬),但对我来说,增加涉及 的代码的可读性Map<Date, Foo>是值得的。

回答by Bohemian

Part 1: "Shouldn't equalsagree with compareTo?*

第 1 部分:“不应该equals同意compareTo吗?*

No; compareTo should agree with equals, but the reverse is irrelevant.

不; compareTo 应该与 equals 一致,但反之则无关紧要。

compareTo is about sorting order. equals is about equality. Consider race cars, that may be sorted by fastest lap time in practice to determine starting position. Equal lap times does not mean they are the same car.

compareTo 是关于排序顺序的。等于是关于平等。考虑赛车,在实践中可以按最快单圈时间排序以确定起始位置。相同的单圈时间并不意味着它们是同一辆车。

Part 2: Equal dates.

第 2 部分:相等日期。

Database calls will return a java.sql.Date, which although it is assignable to java.util.Dare, because it extends that, will not be equal, because the class is different.

数据库调用将返回一个 java.sql.Date,虽然它可以分配给 java.util.Dare,因为它扩展了它,但不会相等,因为类不同。

A work around may be:

解决方法可能是:

java.util.Date test;
java.sql.Date date;
if (date.equals(new java.sql.Date(test.getTime()))

回答by Mureinik

A Dateobject returned from a database would probably be a java.sql.Timestamp, which cannot be equal to a java.util.Dateobject. I'd just take the long returned from getTime(), and use that as a key in your HashMap.

Date从数据库返回的对象可能是 a java.sql.Timestamp,它不能等于java.util.Date对象。我只需要从 返回的很长时间getTime(),然后将其用作HashMap.