Problem
I have a Map<Date, Foo>
, and a list of objects from the database with an effectiveDate
property, and I want to check to see if the Date
keys in my map are equal to any of the effectiveDate
s in the database - if so, do stuff with Foo
.
我有一个Map
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.containsKey
call 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 equals
and compareTo
agree? (I assume the implementation of java.util.Date
at least should try to follow the recommendation of java.lang.Comparable
).
1)平等和比较不应该一致吗?(我假定实现了java.util。日期至少应该遵循java.lang.可比的建议)。
2) The Date#equals
doc says this:
2) # = doc的日期是:
Thus, two Date objects are equal if and only if the getTime method returns the same long value for both.
因此,如果且仅当getTime方法为两者返回相同的长值时,两个Date对象是相等的。
...Looks like the getTime
method returns the same long value for both of these dates, yet equal
returns 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方法对于这两个日期返回相同的长值,但是equal返回false。你知道为什么会这样吗?我搜了很多遍,但没有发现有人描述同样的问题。
P.S. I'm stuck using java.util.Date
. Please don't just recommend JodaTime.
附注:我被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.
附注:我意识到我可以改变这段代码的结构,并让它正常工作。但这是可行的,我不想只是围绕它工作,除非它是已知的问题。似乎错了。
3 个解决方案
#1
7
As Mureinik hinted at and Sotirios Delimanolis pointed out more specifically, the problem here is with the implementation of java.util.Date
.
正如Mureinik和Sotirios Delimanolis更具体地指出的那样,这里的问题在于java.util.Date的实现。
java.util.Date
is extended by 3 classes in the java.sql
package, 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。在java中,日期被延长了3个类。sql包,所有这一切似乎做类似的事情,根本不清楚是谁的区别在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.
现在,在一个看起来很严重的设计缺陷中,有人决定用java.sql使equals()不对称。Timestamp -也就是说,Timestamp .equals(date)可以返回false,即使date.equals(Timestamp)返回true。好主意。
I wrote a few lines to see which java.sql
classes demonstrate this ridiculous property - apparently it's just Timestamp
. This code:
我写了几行来查看哪个java。sql类演示了这个可笑的属性——显然它只是时间戳。这段代码:
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.HashMap
uses 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在实现containsKey()时使用parameters .equals(key)(而不是key.equals(parameter)),这个奇怪的结果出现在给定的情况中。
So, how to get around this?
那么,如何解决这个问题呢?
1) Use a Long
key in the map rather than a Date
(as Mureinik noted) - since java.util.Date
and java.util.Timestamp
return 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)在地图上使用长键而不是日期(如Mureinik指出的),因为java.util。日期和java.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 Foo
each 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
#2
2
Part 1: "Shouldn't equals
agree with compareTo
?*
第一部分:“平等不应该同意比较的观点吗?
No; compareTo should agree with equals, but the reverse is irrelevant.
没有;比较应与平等一致,但反过来则无关紧要。
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。日期,尽管它可以分配给java.util。大胆,因为它延伸了那个,就不会是相等的,因为这个类是不同的。
A work around may be:
可能的工作是:
java.util.Date test;
java.sql.Date date;
if (date.equals(new java.sql.Date(test.getTime()))
#3
2
A Date
object returned from a database would probably be a java.sql.Timestamp
, which cannot be equal to a java.util.Date
object. I'd just take the long returned from getTime()
, and use that as a key in your HashMap
.
从数据库返回的日期对象可能是java.sql。时间戳,它不能等于java.util。日期对象。我只取getTime()返回的long作为HashMap中的键。
#1
7
As Mureinik hinted at and Sotirios Delimanolis pointed out more specifically, the problem here is with the implementation of java.util.Date
.
正如Mureinik和Sotirios Delimanolis更具体地指出的那样,这里的问题在于java.util.Date的实现。
java.util.Date
is extended by 3 classes in the java.sql
package, 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。在java中,日期被延长了3个类。sql包,所有这一切似乎做类似的事情,根本不清楚是谁的区别在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.
现在,在一个看起来很严重的设计缺陷中,有人决定用java.sql使equals()不对称。Timestamp -也就是说,Timestamp .equals(date)可以返回false,即使date.equals(Timestamp)返回true。好主意。
I wrote a few lines to see which java.sql
classes demonstrate this ridiculous property - apparently it's just Timestamp
. This code:
我写了几行来查看哪个java。sql类演示了这个可笑的属性——显然它只是时间戳。这段代码:
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.HashMap
uses 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在实现containsKey()时使用parameters .equals(key)(而不是key.equals(parameter)),这个奇怪的结果出现在给定的情况中。
So, how to get around this?
那么,如何解决这个问题呢?
1) Use a Long
key in the map rather than a Date
(as Mureinik noted) - since java.util.Date
and java.util.Timestamp
return 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)在地图上使用长键而不是日期(如Mureinik指出的),因为java.util。日期和java.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 Foo
each 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
#2
2
Part 1: "Shouldn't equals
agree with compareTo
?*
第一部分:“平等不应该同意比较的观点吗?
No; compareTo should agree with equals, but the reverse is irrelevant.
没有;比较应与平等一致,但反过来则无关紧要。
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。日期,尽管它可以分配给java.util。大胆,因为它延伸了那个,就不会是相等的,因为这个类是不同的。
A work around may be:
可能的工作是:
java.util.Date test;
java.sql.Date date;
if (date.equals(new java.sql.Date(test.getTime()))
#3
2
A Date
object returned from a database would probably be a java.sql.Timestamp
, which cannot be equal to a java.util.Date
object. I'd just take the long returned from getTime()
, and use that as a key in your HashMap
.
从数据库返回的日期对象可能是java.sql。时间戳,它不能等于java.util。日期对象。我只取getTime()返回的long作为HashMap中的键。