More specifically I want an interface to compare objects which can be only compared for equality [e.g. complex numbers] but don\'t have total order on them. It should have [Not
There's no built-in type that is used for this in Java. It's a "hole" in the collections design, IMO. There's the string-specific Collator class which is about as close as it gets, I'm afraid.
There's no way of customizing the built-in maps to use a specific kind of equality comparison, worse luck. It's entirely reasonable to want this functionality, and a real pain that it's not already present.
You could create your own such interface of course, and write your own map variants which use it... but having to do so sucks :(
The type you want is Guava's Equivalence. However, you may be disappointed, as in Java Collection
and Map
are fairly rigidly specified in terms of Object.equals
, and you will not find implementations of those in Guava that use an alternate equivalence. You can, however, simulate that behavior a bit using myEquivalence.wrap(myObject)
.
In the end I opted to write what I do in similar cases. If I need special equality/hash - like for example keeping weak references. You can wrap the key like that. Overall it's not very different from the interface approach but it creates dumb instances (like HashMap/Hashtable for bucket entries). You might need extra unwrapping for keySet() and so on...
package t1;
public abstract class KeyX<Key> implements java.io.Serializable {
private static final long serialVersionUID = 0l;
final Key key;
final int hash;
protected KeyX(Key key){
this.key = key;
this.hash = hashCode(key);
}
protected abstract int hashCode(Key key);
//Key, Key will be way too strict and it'd required, key.getClass().isInstance(y) prior calling
protected abstract boolean equals(Key x, Object y);
@Override
public final boolean equals(Object obj) {
if (obj==this)
return true;
if (!(obj instanceof KeyX)){
return false;
}
final KeyX<?> other = (KeyX<?>) obj;
return this.key==other.key || (hash==other.hash && other.key!=null && equals(this.key, other.key));
}
@Override
public final int hashCode() {
return hash;
}
public final Key unwrap(){
return key;
}
}
I would suggest using a function based approach to generate the buckets like the MultiMaps.index() method in Google collections (now Guava) does. They use a Function<V,K>
which maps objects of type V
to keys of type K
(in your case the buckets).