billroper: (Default)
[personal profile] billroper
Thinking some more about yesterday's Java problem:

In almost every case, for an "ID"-type class, two different instances of the ID that contain the same identifiers should return true when passed to the equals() method and should return identical hash code values when the hashCode() method is called. The problem is that the standard equals() and hashCode() methods that are inherited from the Object class will treat these as different objects. If an ID is supposed to uniquely identify some Object (for example, a PersonID that uniquely identifies a Person), only one instance of the ID will work to identify the unique Object if the ID is used as a key in a HashMap or the like, because a different instance of the ID, although conceptually equal, will generate a different hash code and will return false in an equals() comparison.

The solution, then, is to override the equals() and hashCode() methods for the ID classes so that IDs that are conceptually equal will return identical hash code values and will return true in an equals() comparison. This is, in fact, the case by default if the ID is simply a String.

So let's override the hashCode() and equals() methods for the ID classes. Life is good.

Except now there's no way to identify different instances of your ID object. And when you're writing a serialized stream that may contain duplicate references to the same object, you want to know if you've written an object to the stream before, so you can write the object once and restore the duplicate references to the object when reading the serialized stream back into memory. Normally, you could use a HashMap with the object as a key to keep track of this, but now two different instances of the ID will return the same hash code and will return true for equals(), so different instances are interpreted as being the same instance.

It looks like the right answer to this is to define an interface that all of the serializable objects can implement that will, by default, call back to the default object implementations of hashCode() and equals() (let's call them objectHashCode() and objectEquals()); then implement an ObjectHashMap that will call objectHashCode() and objectEquals() instead of hashCode() and equals().

*sigh*

This is an annoying amount of work to do...

Date: 2014-06-29 08:12 pm (UTC)
madfilkentist: My cat Florestan (gray shorthair) (hex)
From: [personal profile] madfilkentist
Having, in effect, two different versions of equals for the same class sounds error-prone to me. I'd be more inclined, if it's possible, to define a wrapper object with definitions of equals and hashCode that distinguish every instance, and compare and hash (new DistinsuishingWrapper(object)) whenever you need that behavior.

The obvious downside is that it's a little more overhead to create and garbage-collect the wrappers. This may or may not be a deal-breaker in your application.

Date: 2014-06-29 08:20 pm (UTC)
From: [identity profile] catsittingstill.livejournal.com
It sounds very tedious and detail-oriented to me. You have my sympathy. But I suppose if anyone could do it / was willing to do it, it wouldn't pay very well.

Date: 2014-06-30 10:37 am (UTC)
madfilkentist: My cat Florestan (gray shorthair) (hex)
From: [personal profile] madfilkentist
The last place where I worked banned Apache Commons as oudated and unreliable (and favored new Java EE technologies supported by a single person as fundamental building blocks). What are your thoughts on the reliability of Commons?

Of course, Java 7's IdentityHashMap makes the point moot in this case.
Edited Date: 2014-06-30 10:37 am (UTC)

Date: 2014-06-29 09:42 pm (UTC)
mdlbear: blue fractal bear with text "since 2002" (Default)
From: [personal profile] mdlbear
On the gripping hand, is there any good reason why two equal IDs should be different objects? In languages with a built-in identifier type (Lisp, Smalltalk, ...) IDs are singletons, meaning you can use == to test for equality instead of equals. Big win for speed.

To make this work, put a hash table in your class and use a factory method instead of new.

Date: 2014-06-29 10:12 pm (UTC)
mdlbear: blue fractal bear with text "since 2002" (Default)
From: [personal profile] mdlbear
Right; I'd forgotten that. In that case, IdentityMap is definitely your friend. There's a lot of good stuff in the Apache Commons library.

You'll also want to check out Lombok and findbugs -- the latter is kind of a super-lint for Java.
Edited Date: 2014-06-29 10:13 pm (UTC)

Profile

billroper: (Default)
billroper

January 2026

S M T W T F S
     1 2 3
4 5 6 7 8910
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 3031

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 31st, 2026 02:14 am
Powered by Dreamwidth Studios