I have a Map, and a list of objects from the database with an effectiveDate property, and I want to check to see
As Mureinik hinted at and Sotirios Delimanolis pointed out more specifically, the problem here is with the implementation of 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.
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.
I wrote a few lines to see which java.sql classes demonstrate this ridiculous property - apparently it's just Timestamp. This code:
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.
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.
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:
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 is worth it.