@Retention(value=SOURCE) @Target(value=TYPE) public @interface CustomKeyEquivalence
Map
- or Set
-like class or interface with a reference
key type uses a custom
equivalence strategy when comparing keys. If this annotation is applied to a @KolobokeMap
- or @KolobokeSet
-annotated class or interface,
Koloboke Compile checks that
boolean |
keyEquals(KeyType queriedKey, KeyType keyInContainer) |
int |
keyHashCode(KeyType key) |
keyEquals()
and keyHashCode()
must obey general equivalence relation rules
(see Object.equals(Object)
and Object.hashCode()
specifications for details).
Koloboke Compile provides the following guarantees on how it calls keyEquals()
and
keyHashCode()
inside generated implementations:
keyEquals()
are never null
.keyHashCode()
is never null
.keyEquals()
call is present in the map (or the set) or
used to be present, but removed later. It means that if the implemented type has some
restrictions on the keys that it may contain, those restrictions apply to the second
argument. From performance perspective, since the second argument has already been inserted
into the set or map, if it caches hashcode a-la String
, the keyEquals()
implementation may benefit from the knowledge that the hashcode of the second argument
is already computed and cached.keyEquals()
are never identical (i. e. not references to the same
object). In particular, this means that to have IdentityHashMap
-like behaviour
keyEquals()
should be implemented as just returning false
:
@KolobokeMap
@CustomKeyEquivalence
abstract class IdentityToIntMap<K> implements Map<K, Integer> {
static <K> IdentityToIntMap<K> withExpectedSize(int expectedSize) {
return new KolobokeIdentityToIntMap<K>(expectedSize);
}
abstract int getInt(K key);
abstract int put(K key, int value);
final boolean keyEquals(K queriedKey, K keyInMap) {
return false;
}
final int keyHashCode(K key) {
return System.identityHashCode(key);
}
}
keyEquals()
and keyHashCode()
don't belong to any public interface provided
by the Koloboke Collections API or Koloboke Compile. So @CustomKeyEquivalence
is kind
of a substitute of the @Override
annotation for these methods. Koloboke Compile just
checks that methods are present, accessible from the generated subclass and have proper argument
types. It's a user responsibility to make these methods consistent with each other and
providing a valid equivalence relationship in the domain of the keys. See examples of valid
equivalences in the documentation to the following methods: Equivalence.charSequence()
,
Equivalence.caseInsensitive()
.
If the annotated type implements ObjSet
or ObjObjMap
or other
ObjXxxMap
interface from the Koloboke Collections API, and it declares equals()
method abstract to make Koloboke Compile implement it, or it declares keySet()
or entrySet()
methods abstract and expects Koloboke Compile
to implement equals()
methods correctly in the returned Set
views of the map,
it's a user responsibility to implement ObjCollection.equivalence()
or ObjObjMap.keyEquivalence()
or similar method in the extended
ObjXxxMap
interface and return from it an Equivalence
object that reflects keyEquals()
and keyHashCode()
strategy. For example, here is how a @KolobokeMap
with configurable key equivalence should be declared:
@KolobokeMap
@CustomKeyEquivalence
abstract class ConfigurableKeyEquivalenceMap<K> implements HashObjLongMap<K> {
static <K> HashObjLongMap<K> with(
@Nonnull Equivalence<? super K> keyEquivalence, int expectedSize) {
return new KolobokeConfigurableKeyEquivalenceMap<K>(keyEquivalence, expectedSize);
}
@Nonnull
private final Equivalence<? super K> keyEquivalence;
ConfigurableKeyEquivalenceMap(@Nonnull Equivalence<? super K> keyEquivalence) {
this.keyEquivalence = keyEquivalence;
}
final boolean keyEquals(K queriedKey, K keyInMap) {
return keyEquivalence.equivalent(queriedKey, keyInMap);
}
final int keyHashCode(K key) {
return keyEquivalence.hash(key);
}
@SuppressWarnings("unchecked")
@Nonnull
@Override
public final Equivalence<K> keyEquivalence() {
return (Equivalence<K>) keyEquivalence;
}
}
In the Koloboke Collections's API, custom key equivalence strategy for sets or maps is
configured by methods HashObjSetFactory.withEquivalence(Equivalence)
or HashObjObjMapFactory.withKeyEquivalence(Equivalence)
or similar methods in HashObjXxxMapFactory
interfaces.