views:

109

answers:

3

I need to create a Set of objects. The concern is I do not want to base the hashing or the equality on the objects' hashCode and equals implementation. Instead, I want the hash code and equality to be based only on each object's reference identity (i.e.: the value of the reference pointer).

I'm not sure how to do this in Java.

The reasoning behind this is my objects do not reliably implement equals or hashCode, and in this case reference identity is good enough.

+1  A: 

You can extend HashSet (or actually - AbstractSet) , and back it with IdentityHashMap which uses System.identityHashCode(object) instead of obj.hashCode().

You can simply google for IdentityHashSet, there are some implementations already. Or use Collections.newSetFromMap(..) as suggested by Joachim Sauer.

This of course should be done only if you are not in "possession" of your objects' classes. Otherwise just fix their hashCode()

Bozho
Seems like it would be easier to override `hashCode()` than to extend `HashSet`.
danben
if the objects aren't at his "possession"
Bozho
Right - it sounds like they are but I could be mistaken.
danben
The objects are Hibernate entity beans. It's not possible to correctly implement these functions, because doing so 1. requires an open hibernate session, and 2. will potentially traverse the entire database!
landon9720
don't hibernate entity beans generally come with a primary key included?
wds
@landon9720 in that case show use a bit of these entities - you may have used the wrong fields to implement `hashCode()`
Bozho
+11  A: 

I guess that java.util.IdentityHashMap is what you're looking for (note, there's no IdentityHashSet). Lookup the API documentation:

This class implements the Map interface with a hash table, using reference-equality in place of object-equality when comparing keys (and values). In other words, in an IdentityHashMap, two keys k1 and k2 are considered equal if and only if (k1==k2). (In normal Map implementations (like HashMap) two keys k1 and k2 are considered equal if and only if (k1==null ? k2==null : k1.equals(k2)).)

This class is not a general-purpose Map implementation! While this class implements the Map interface, it intentionally violates Map's general contract, which mandates the use of the equals method when comparing objects. This class is designed for use only in the rare cases wherein reference-equality semantics are required.

edit: See Joachim Sauer's comment below, it's really easy to make a Set based on a certain Map. You'd need to do something like this:

Set<E> mySet = Collections.newSetFromMap(new IdentityHashMap<E, Boolean>());
Jesper
...except that a `Map` interface is completely different usage from a `Set` interface.
Matt Ball
Not really, a Set is just a Map where you ignore the values. You could easily write a wrapper to make it look more like a `Set` if you don't want to deal with it all the time in your application.
Jesper
+1 - It should be easy to implement a Set based on a Map implementation. That is how HashSet is implemented ...
Stephen C
There's even a method in the JDK to create a `Set` with a specified backing `Map` and it's called `newSetFromMap`: http://java.sun.com/javase/6/docs/api/java/util/Collections.html#newSetFromMap(java.util.Map%29
Joachim Sauer
@Carl: You can look at the source code of `java.util.HashSet` (which you can find in the file **src.zip** in your JDK installation dir) and you'll see that it uses a `HashMap` under the covers (it's implemented in terms of `HashMap`, as Stephen C says).
Jesper
@jesper: I stand corrected. Especially with Joachim Sauer's suggestion, this looks like a winner.
Carl Smotricz
Also `Map.getKeySet()` will provide you with a Set from the `IdentityHashMap` without a custom implementation.
Daniel
@Daniel: while the `Set` returned by `keySet()` is a view that represents the current data in the `Map` at all times and it *does* support removal methods, it doesn't support adding anything. That's simply because adding anything to a `keySet()` would leave the value of the entry undefined: http://java.sun.com/javase/6/docs/api/java/util/Map.html#keySet(%29
Joachim Sauer
@Joachim Well spotted!
Daniel
+3  A: 

You could wrap your objects into a wrapper class which could then implement hashcode and equals based simply on the object's identity.

Carl Smotricz
+1 - this will be feasible where he cannot fix the methods, and extending the existing class is not an option.
Stephen C
And if you're too lazy to write your own wrapper class, just use a 1-element `Object[]`. Ugly, but works.
polygenelubricants
@polygenelubricants: Clever! That hadn't occurred to me.
Carl Smotricz
@Carl: just found something that may be better: http://java.sun.com/javase/6/docs/api/java/util/concurrent/atomic/AtomicReference.html
polygenelubricants
@polygenelubricants: Here I have some trouble following you. As I understand it, AtomicReferences have a vastly different purpose. Yes, they wrap, but I suspect they contain all kinds of unrelated plumbing to do their concurrency-taming magic. I haven't looked closely, though.
Carl Smotricz
@Carl: I've never actually used it myself (this is a comment for discussion, not an authoritatively recommending answer), but it inherits `equals` and `hashCode` from `Object`, and it already has `get` and `set` which makes it a wrapper that works in this case. We don't really need its atomicity features, but it wouldn't hurt either. It may still count as an abusive use of a library class, but it's not as ugly as using `new Object[1]` arrays.
polygenelubricants