diff --git a/CHANGES.txt b/CHANGES.txt
index f14e3d1b9..3c3e400eb 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,7 @@
+[0.7.2]
+** API-breaking changes
+HPPCRT-46: Remove CurstomHash and replace by equals/hashCode overrides in normal hash containers.
+
[0.7.1]
** Bug fixes
HPPCRT-45: API with intervals arguments is inconsistent with JDK conventions.
diff --git a/README.txt b/README.txt
index a3678c4fe..32a4fa2a7 100644
--- a/README.txt
+++ b/README.txt
@@ -45,7 +45,7 @@ Stable version is available on Maven:
com.github.vsonnierhppcrt
- 0.7.1
+ 0.7.2
````
diff --git a/hppcrt-benchmarks/pom.xml b/hppcrt-benchmarks/pom.xml
index f26e56bba..42a979c8c 100644
--- a/hppcrt-benchmarks/pom.xml
+++ b/hppcrt-benchmarks/pom.xml
@@ -5,13 +5,13 @@
com.github.vsonnierhppcrt-parent
- 0.7.2-SNAPSHOT
+ 0.7.2../pom.xmlhppcrt-benchmarks
- 0.7.2-SNAPSHOT
+ 0.7.2jarHPPC-RT Benchmarks
@@ -24,14 +24,14 @@
2.6UTF-8
- 1.11
+ 1.11.11.7benchmarks0.7.1
- 7.0.6
+ 7.0.76.2.0
- 0.6.7
+ 0.6.86.0.0
diff --git a/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/implementations/HashMapImplementations.java b/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/implementations/HashMapImplementations.java
index 1d8b581a9..34e91477d 100644
--- a/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/implementations/HashMapImplementations.java
+++ b/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/implementations/HashMapImplementations.java
@@ -79,20 +79,6 @@ public boolean isHashQualityApplicable() {
}
},
- HPPCRT_OBJ_INT_STRATEGY
- {
- @Override
- public MapImplementation> getInstance(final int size, final float loadFactor)
- {
- return new HppcrtObjectIntCustomMap(size, loadFactor);
- }
-
- @Override
- public boolean isHashQualityApplicable() {
-
- return true;
- }
- },
HPPC_OBJ_INT {
@Override
diff --git a/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/implementations/HppcrtObjectIntCustomMap.java b/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/implementations/HppcrtObjectIntCustomMap.java
deleted file mode 100644
index c40220e20..000000000
--- a/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/implementations/HppcrtObjectIntCustomMap.java
+++ /dev/null
@@ -1,152 +0,0 @@
-package com.carrotsearch.hppcrt.implementations;
-
-import java.util.Random;
-
-import org.openjdk.jmh.infra.Blackhole;
-
-import com.carrotsearch.hppcrt.XorShift128P;
-import com.carrotsearch.hppcrt.maps.ObjectIntCustomHashMap;
-import com.carrotsearch.hppcrt.strategies.ObjectHashingStrategy;
-
-public class HppcrtObjectIntCustomMap extends MapImplementation>
-{
-
- private ComparableInt[] insertKeys;
- private ComparableInt[] containsKeys;
- private ComparableInt[] removedKeys;
- private int[] insertValues;
-
- protected HppcrtObjectIntCustomMap(final int size, final float loadFactor)
- {
- super(new ObjectIntCustomHashMap(size, loadFactor,
- //A good behaved startegy that compensates bad hashCode() implementation.
- new ObjectHashingStrategy() {
-
- @Override
- public int computeHashCode(final MapImplementation.ComparableInt object) {
-
- //eat some CPU to simulate method cost
- Blackhole.consumeCPU(MapImplementation.METHOD_CALL_CPU_COST);
-
- return object.value;
- }
-
- @Override
- public boolean equals(final MapImplementation.ComparableInt o1, final MapImplementation.ComparableInt o2) {
-
- //eat some CPU to simulate method cost
- Blackhole.consumeCPU(MapImplementation.METHOD_CALL_CPU_COST);
-
- return o1.value == o2.value;
- }
- }));
- }
-
- /**
- * Setup
- */
- @Override
- public void setup(final int[] keysToInsert, final MapImplementation.HASH_QUALITY hashQ, final int[] keysForContainsQuery, final int[] keysForRemovalQuery) {
-
- final Random prng = new XorShift128P(0x122335577L);
-
- this.insertKeys = new ComparableInt[keysToInsert.length];
-
- this.containsKeys = new ComparableInt[keysForContainsQuery.length];
- this.removedKeys = new ComparableInt[keysForRemovalQuery.length];
-
- this.insertValues = new int[keysToInsert.length];
-
- //Auto box into Integers, they must have the same length anyway.
- for (int i = 0; i < keysToInsert.length; i++) {
-
- this.insertKeys[i] = new ComparableInt(keysToInsert[i], hashQ);
-
- this.insertValues[i] = prng.nextInt();
- }
-
- //Auto box into Integers
- for (int i = 0; i < keysForContainsQuery.length; i++) {
-
- this.containsKeys[i] = new ComparableInt(keysForContainsQuery[i], hashQ);
- }
-
- //Auto box into Integers
- for (int i = 0; i < keysForRemovalQuery.length; i++) {
-
- this.removedKeys[i] = new ComparableInt(keysForRemovalQuery[i], hashQ);
- }
- }
-
- @Override
- public void clear() {
- this.instance.clear();
- }
-
- @Override
- public int size() {
-
- return this.instance.size();
- }
-
- @Override
- public int benchPutAll() {
-
- final ObjectIntCustomHashMap instance = this.instance;
-
- final int[] values = this.insertValues;
-
- int count = 0;
-
- final ComparableInt[] keys = this.insertKeys;
-
- for (int i = 0; i < keys.length; i++) {
-
- count += instance.put(keys[i], values[i]);
- }
-
- return count;
- }
-
- @Override
- public int benchContainKeys()
- {
- final ObjectIntCustomHashMap instance = this.instance;
-
- int count = 0;
-
- final ComparableInt[] keys = this.containsKeys;
-
- for (int i = 0; i < keys.length; i++) {
-
- count += instance.containsKey(keys[i]) ? 1 : 0;
- }
-
- return count;
- }
-
- @Override
- public int benchRemoveKeys() {
-
- final ObjectIntCustomHashMap instance = this.instance;
-
- int count = 0;
-
- final ComparableInt[] keys = this.removedKeys;
-
- for (int i = 0; i < keys.length; i++) {
-
- count += instance.remove(keys[i]);
- }
-
- return count;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public void setCopyOfInstance(final MapImplementation> toCloneFrom) {
-
- this.instance = ((ObjectIntCustomHashMap) toCloneFrom.instance).clone();
-
- }
-}
\ No newline at end of file
diff --git a/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/misc/HppcMapSyntheticBench.java b/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/misc/HppcMapSyntheticBench.java
index 16ea3182a..7bd2ba810 100644
--- a/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/misc/HppcMapSyntheticBench.java
+++ b/hppcrt-benchmarks/src/main/java/com/carrotsearch/hppcrt/misc/HppcMapSyntheticBench.java
@@ -15,11 +15,9 @@
import com.carrotsearch.hppcrt.lists.LongArrayList;
import com.carrotsearch.hppcrt.lists.ObjectArrayList;
import com.carrotsearch.hppcrt.maps.IntLongHashMap;
-import com.carrotsearch.hppcrt.maps.ObjectLongCustomHashMap;
import com.carrotsearch.hppcrt.maps.ObjectLongHashMap;
import com.carrotsearch.hppcrt.maps.ObjectLongIdentityHashMap;
import com.carrotsearch.hppcrt.procedures.LongProcedure;
-import com.carrotsearch.hppcrt.strategies.ObjectHashingStrategy;
public final class HppcMapSyntheticBench
{
@@ -137,25 +135,7 @@ public boolean equals(final Object obj)
}
}
- /**
- * Testing for strategies
- */
- private final ObjectHashingStrategy INTHOLDER_TRIVIAL_STRATEGY = new ObjectHashingStrategy() {
-
- @Override
- public int computeHashCode(final ComparableInt o)
- {
- return o.value;
- }
-
- @Override
- public boolean equals(final ComparableInt o1, final ComparableInt o2)
- {
- return o1.value == o2.value;
- }
- };
-
-/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor
*/
@@ -732,14 +712,6 @@ ObjectLongHashMap. newInstance(HppcMapSyntheticBench.COUNT, HashC
getKind, HASH_QUALITY.BAD, dis);
System.gc();
}
-
- // use specialized strategy to overcome the bad hash behaviour above.
- runMapIntegerObjectLong("ObjectLongOpenCustomHashMap with strategy",
- ObjectLongCustomHashMap. newInstance(
- HppcMapSyntheticBench.COUNT, HashContainers.DEFAULT_LOAD_FACTOR,
- this.INTHOLDER_TRIVIAL_STRATEGY),
- HppcMapSyntheticBench.COUNT, HashContainers.DEFAULT_LOAD_FACTOR, getKind, HASH_QUALITY.BAD, Distribution.HIGHBITS);
- System.gc();
}
public void runMapIterationBench()
@@ -756,26 +728,26 @@ private Generator getGenerator(final Distribution disKind, final int pushedEleme
switch (disKind)
{
- case RANDOM:
- generator = disGene.RANDOM;
- break;
+ case RANDOM:
+ generator = disGene.RANDOM;
+ break;
- case CONTIGUOUS:
- generator = disGene.LINEAR;
- break;
+ case CONTIGUOUS:
+ generator = disGene.LINEAR;
+ break;
- case HIGHBITS:
- generator = disGene.HIGHBITS;
- break;
+ case HIGHBITS:
+ generator = disGene.HIGHBITS;
+ break;
- default:
- throw new RuntimeException();
+ default:
+ throw new RuntimeException();
}
return generator;
}
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* main
*/
diff --git a/hppcrt-template-processor/pom.xml b/hppcrt-template-processor/pom.xml
index fb5fd6eda..232e52745 100644
--- a/hppcrt-template-processor/pom.xml
+++ b/hppcrt-template-processor/pom.xml
@@ -5,14 +5,14 @@
com.github.vsonnierhppcrt-parent
- 0.7.2-SNAPSHOT
+ 0.7.2../pom.xmlcom.github.vsonnierhppcrt-template-processor
- 0.7.2-SNAPSHOT
+ 0.7.2jarHPPC-RT Template Processor
diff --git a/hppcrt/pom.xml b/hppcrt/pom.xml
index 4fe52b2c8..36cde52b2 100644
--- a/hppcrt/pom.xml
+++ b/hppcrt/pom.xml
@@ -6,14 +6,14 @@
com.github.vsonnierhppcrt-parent
- 0.7.2-SNAPSHOT
+ 0.7.2../pom.xmlcom.github.vsonnierhppcrt
- 0.7.2-SNAPSHOT
+ 0.7.2jarHPPC-RT Collections
diff --git a/hppcrt/src/main/templates/com/carrotsearch/hppcrt/maps/KTypeVTypeCustomHashMap.java b/hppcrt/src/main/templates/com/carrotsearch/hppcrt/maps/KTypeVTypeCustomHashMap.java
deleted file mode 100644
index ab18017c6..000000000
--- a/hppcrt/src/main/templates/com/carrotsearch/hppcrt/maps/KTypeVTypeCustomHashMap.java
+++ /dev/null
@@ -1,1829 +0,0 @@
-package com.carrotsearch.hppcrt.maps;
-
-import com.carrotsearch.hppcrt.*;
-import com.carrotsearch.hppcrt.cursors.*;
-import com.carrotsearch.hppcrt.predicates.*;
-import com.carrotsearch.hppcrt.procedures.*;
-import com.carrotsearch.hppcrt.strategies.*;
-import com.carrotsearch.hppcrt.hash.*;
-
-/*! #import("com/carrotsearch/hppcrt/Intrinsics.java") !*/
-/*! ${TemplateOptions.doNotGenerateKType("byte", "char", "short", "float", "double" )} !*/
-/*! #set( $ROBIN_HOOD_FOR_ALL = true) !*/
-/*! #set( $DEBUG = false) !*/
-//If RH is defined, RobinHood Hashing is in effect :
-/*! #set( $RH = $ROBIN_HOOD_FOR_ALL) !*/
-
-/**
- * A hash map of KType to VType, implemented using open
- * addressing with linear probing for collision resolution.
- *
- * The difference with {@link KTypeVTypeHashMap} is that it uses a
- * {@link KTypeHashingStrategy} to compare objects externally instead of using
- * the built-in hashCode() / equals(). In particular, the management of null
- * keys is up to the {@link KTypeHashingStrategy} implementation.
- * The internal buffers of this implementation ({@link #keys}, {@link #values})
- * are always allocated to the nearest size that is a power of two. When
- * the capacity exceeds the given load factor, the buffer size is doubled.
- *
- *
- *
Important note. The implementation uses power-of-two tables and linear
- * probing, which may cause poor performance (many collisions) if hash values are
- * not properly distributed. Therefore, it is up to the {@link KTypeHashingStrategy} to
- * assure good performance.
- *
-#if ($TemplateOptions.KTypeGeneric)
- *
This implementation supports null keys. In addition, objects passed to the {@link KTypeHashingStrategy} are guaranteed to be not-null.
-#end
-#if ($TemplateOptions.VTypeGeneric)
- *
This implementation supports null values.
-#end
- *
- *
-#if ($RH)
- *
Robin-Hood hashing algorithm is also used to minimize variance
- * in insertion and search-related operations, for an all-around smother operation at the cost
- * of smaller peak performance:
- *
- Pedro Celis (1986) for the original Robin-Hood hashing paper,
- * Direct map iteration: iterate {keys[i], values[i]} for i in [0; keys.length[ where keys[i] != 0/null, then also
- * {0/null, {@link #allocatedDefaultKeyValue} } is in the map if {@link #allocatedDefaultKey} = true.
- *
- */
- public/*! #if ($TemplateOptions.KTypePrimitive)
- KType []
- #else !*/
- Object[]
- /*! #end !*/
- keys;
-
- /**
- * Hash-indexed array holding all values associated to the keys.
- * stored in {@link #keys}.
- */
- public/*! #if ($TemplateOptions.VTypePrimitive)
- VType []
- #else !*/
- Object[]
- /*! #end !*/
- values;
-
- /*! #if ($RH) !*/
- /**
- * #if ($RH)
- * Caches the hash value = hash(strategy, keys[i]) & mask, if keys[i] != 0/null,
- * for every index i.
- * #end
- * @see #assigned
- */
- /*! #end !*/
- /*! #if ($RH) !*/
- protected int[] hash_cache;
- /*! #end !*/
-
- /**
- * True if key = 0/null is in the map.
- */
- public boolean allocatedDefaultKey = false;
-
- /**
- * if allocatedDefaultKey = true, contains the associated V to the key = 0/null
- */
- public VType allocatedDefaultKeyValue;
-
- /**
- * Cached number of assigned slots in {@link #keys}.
- */
- protected int assigned;
-
- /**
- * The load factor for this map (fraction of allocated slots
- * before the buffers must be rehashed or reallocated).
- */
- protected final double loadFactor;
-
- /**
- * Resize buffers when {@link #keys} hits this value.
- */
- private int resizeAt;
-
- /**
- * Per-instance size perturbation
- * introduced in rehashing to create a unique key distribution.
- */
- private final int perturbation = Containers.randomSeed32();
-
- /**
- * Custom hashing strategy :
- * comparisons and hash codes of keys will be computed
- * with the strategy methods instead of the native Object equals() and hashCode() methods.
- */
- protected final KTypeHashingStrategy super KType> hashStrategy;
-
- /**
- * Creates a hash map with the default capacity of {@link Containers#DEFAULT_EXPECTED_ELEMENTS},
- * load factor of {@link HashContainers#DEFAULT_LOAD_FACTOR}, using the hashStrategy as {@link KTypeHashingStrategy}
- *
- *
See class notes about hash distribution importance.
- */
- public KTypeVTypeCustomHashMap(final KTypeHashingStrategy super KType> hashStrategy) {
- this(Containers.DEFAULT_EXPECTED_ELEMENTS, hashStrategy);
- }
-
- /**
- * Creates a hash map with the given initial capacity, default load factor of
- * {@link HashContainers#DEFAULT_LOAD_FACTOR}, using the hashStrategy as {@link KTypeHashingStrategy}
- *
- * @param initialCapacity Initial capacity (greater than zero and automatically
- * rounded to the next power of two).
- */
- public KTypeVTypeCustomHashMap(final int initialCapacity, final KTypeHashingStrategy super KType> hashStrategy) {
- this(initialCapacity, HashContainers.DEFAULT_LOAD_FACTOR, hashStrategy);
- }
-
- /**
- * Creates a hash map with the given initial capacity,
- * load factor, using the hashStrategy as {@link KTypeHashingStrategy}
- *
- * @param loadFactor The load factor (greater than zero and smaller than 1).
- *
- *
- */
- public KTypeVTypeCustomHashMap(final int initialCapacity, final double loadFactor,
- final KTypeHashingStrategy super KType> hashStrategy) {
-
- //only accept not-null strategies.
- if (hashStrategy == null) {
-
- throw new IllegalArgumentException("KTypeVTypeOpenCustomHashMap() cannot have a null hashStrategy !");
- }
-
- this.hashStrategy = hashStrategy;
-
- this.loadFactor = loadFactor;
- //take into account of the load factor to guarantee no reallocations before reaching initialCapacity.
- allocateBuffers(HashContainers.minBufferSize(initialCapacity, loadFactor));
- }
-
- /**
- * Creates a hash map from all key-value pairs of another container.
- */
- public KTypeVTypeCustomHashMap(final KTypeVTypeAssociativeContainer container,
- final KTypeHashingStrategy super KType> hashStrategy) {
- this(container.size(), hashStrategy);
- putAll(container);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public VType put(KType key, VType value) {
- if (Intrinsics. isEmpty(key)) {
-
- if (this.allocatedDefaultKey) {
-
- final VType previousValue = this.allocatedDefaultKeyValue;
- this.allocatedDefaultKeyValue = value;
-
- return previousValue;
- }
-
- this.allocatedDefaultKeyValue = value;
- this.allocatedDefaultKey = true;
-
- return this.defaultValue;
- }
-
- final int mask = this.keys.length - 1;
-
- final KTypeHashingStrategy super KType> strategy = this.hashStrategy;
-
- final KType[] keys = Intrinsics. cast(this.keys);
- int slot = REHASH(strategy, key) & mask;
- KType existing;
-
- /*! #if ($RH) !*/
- final VType[] values = Intrinsics. cast(this.values);
-
- final int[] cached = this.hash_cache;
-
- KType tmpKey;
- VType tmpValue;
- int tmpAllocated;
- int initial_slot = slot;
- int dist = 0;
- int existing_distance = 0;
- /*! #end !*/
-
- while (!Intrinsics. isEmpty(existing = keys[slot])) {
-
- if (strategy.equals(key, existing)) {
- final VType oldValue = Intrinsics. cast(this.values[slot]);
- values[slot] = value;
-
- return oldValue;
- }
-
- /*! #if ($RH) !*/
- //re-shuffle keys to minimize variance
- existing_distance = probe_distance(slot, cached);
-
- if (dist > existing_distance) {
- //swap current (key, value, initial_slot) with slot places
- tmpKey = keys[slot];
- keys[slot] = key;
- key = tmpKey;
-
- tmpAllocated = cached[slot];
- cached[slot] = initial_slot;
- initial_slot = tmpAllocated;
-
- tmpValue = values[slot];
- values[slot] = value;
- value = tmpValue;
-
- /*! #if($DEBUG) !*/
- //Check invariants
-
- assert cached[slot] == (REHASH(strategy, keys[slot]) & mask);
- assert initial_slot == (REHASH(strategy, key) & mask);
- /*! #end !*/
-
- dist = existing_distance;
- }
- /*! #end !*/
-
- slot = (slot + 1) & mask;
-
- /*! #if ($RH) !*/
- dist++;
- /*! #end !*/
- } //end while
-
- // Check if we need to grow. If so, reallocate new data, fill in the last element
- // and rehash.
- if (this.assigned == this.resizeAt) {
- expandAndPut(key, value, slot);
- } else {
- this.assigned++;
- /*! #if ($RH) !*/
- cached[slot] = initial_slot;
- /*! #end !*/
-
- keys[slot] = key;
- values[slot] = value;
-
- /*! #if ($RH) !*/
- /*! #if($DEBUG) !*/
- //Check invariants
- assert cached[slot] == (REHASH(strategy, keys[slot]) & mask);
-
- /*! #end !*/
- /*! #end !*/
- }
- return this.defaultValue;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int putAll(final KTypeVTypeAssociativeContainer extends KType, ? extends VType> container) {
- return putAll((Iterable extends KTypeVTypeCursor extends KType, ? extends VType>>) container);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int putAll(final Iterable extends KTypeVTypeCursor extends KType, ? extends VType>> iterable) {
- final int count = this.size();
-
- for (final KTypeVTypeCursor extends KType, ? extends VType> c : iterable) {
- put(c.key, c.value);
- }
- return this.size() - count;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean putIfAbsent(final KType key, final VType value) {
- if (!containsKey(key)) {
- put(key, value);
- return true;
- }
- return false;
- }
-
- /*! #if ($TemplateOptions.VTypePrimitive) !*/
- /**
- * If key exists, putValue is inserted into the map,
- * otherwise any existing value is incremented by additionValue.
- *
- * @param key
- * The key of the value to adjust.
- * @param putValue
- * The value to put if key does not exist.
- * @param incrementValue
- * The value to add to the existing value if key exists.
- * @return Returns the current value associated with key (after
- * changes).
- */
- @SuppressWarnings({ "cast" })
- @Override
- public VType putOrAdd(final KType key, VType putValue, final VType incrementValue) {
-
- if (containsKey(key)) {
- putValue = get(key);
-
- putValue = (VType) (Intrinsics. add(putValue, incrementValue));
- }
-
- put(key, putValue);
- return putValue;
- }
-
- /*! #end !*/
-
- /*! #if ($TemplateOptions.VTypePrimitive) !*/
- /**
- * Adds incrementValue to any existing value for the given key
- * or inserts incrementValue if key did not previously exist.
- *
- * @param key The key of the value to adjust.
- * @param incrementValue The value to put or add to the existing value if key exists.
- * @return Returns the current value associated with key (after changes).
- */
- @Override
- public VType addTo(final KType key, final VType incrementValue)
- {
- return putOrAdd(key, incrementValue, incrementValue);
- }
-
- /*! #end !*/
-
- /**
- * Expand the internal storage buffers (capacity) and rehash.
- */
- private void expandAndPut(final KType pendingKey, final VType pendingValue, final int freeSlot) {
- assert this.assigned == this.resizeAt;
-
- //default sentinel value is never in the keys[] array, so never trigger reallocs
- assert !Intrinsics. isEmpty(pendingKey);
-
- // Try to allocate new buffers first. If we OOM, it'll be now without
- // leaving the data structure in an inconsistent state.
- final KType[] oldKeys = Intrinsics. cast(this.keys);
- final VType[] oldValues = Intrinsics. cast(this.values);
-
- allocateBuffers(HashContainers.nextBufferSize(this.keys.length, this.assigned, this.loadFactor));
-
- // We have succeeded at allocating new data so insert the pending key/value at
- // the free slot in the old arrays before rehashing.
-
- this.assigned++;
-
- oldKeys[freeSlot] = pendingKey;
- oldValues[freeSlot] = pendingValue;
-
- //for inserts
- final int mask = this.keys.length - 1;
-
- final KTypeHashingStrategy super KType> strategy = this.hashStrategy;
-
- KType key = Intrinsics. empty();
- VType value = Intrinsics. empty();
-
- int slot = -1;
-
- final KType[] keys = Intrinsics. cast(this.keys);
- final VType[] values = Intrinsics. cast(this.values);
-
- /*! #if ($RH) !*/
- final int[] cached = this.hash_cache;
- /*! #end !*/
-
- /*! #if ($RH) !*/
- KType tmpKey = Intrinsics. empty();
- VType tmpValue = Intrinsics. empty();
- int tmpAllocated = -1;
- int initial_slot = -1;
- int dist = -1;
- int existing_distance = -1;
- /*! #end !*/
-
- //iterate all the old arrays to add in the newly allocated buffers
- //It is important to iterate backwards to minimize the conflict chain length !
- final int perturb = this.perturbation;
-
- for (int i = oldKeys.length; --i >= 0;) {
-
- if (!Intrinsics. isEmpty(key = oldKeys[i])) {
-
- value = oldValues[i];
-
- slot = REHASH2(strategy, key, perturb) & mask;
-
- /*! #if ($RH) !*/
- initial_slot = slot;
- dist = 0;
- /*! #end !*/
-
- while (is_allocated(slot, keys)) {
- /*! #if ($RH) !*/
- //re-shuffle keys to minimize variance
- existing_distance = probe_distance(slot, cached);
-
- if (dist > existing_distance) {
- //swap current (key, value, initial_slot) with slot places
- tmpKey = keys[slot];
- keys[slot] = key;
- key = tmpKey;
-
- tmpAllocated = cached[slot];
- cached[slot] = initial_slot;
- initial_slot = tmpAllocated;
-
- tmpValue = values[slot];
- values[slot] = value;
- value = tmpValue;
-
- /*! #if($DEBUG) !*/
- //Check invariants
- assert cached[slot] == (REHASH(strategy, keys[slot]) & mask);
- assert initial_slot == (REHASH(strategy, key) & mask);
- /*! #end !*/
-
- dist = existing_distance;
- }
- /*! #end !*/
-
- slot = (slot + 1) & mask;
-
- /*! #if ($RH) !*/
- dist++;
- /*! #end !*/
- } //end while
-
- /*! #if ($RH) !*/
- cached[slot] = initial_slot;
- /*! #end !*/
-
- keys[slot] = key;
- values[slot] = value;
-
- /*! #if ($RH) !*/
- /*! #if($DEBUG) !*/
- //Check invariants
- assert cached[slot] == (REHASH(strategy, keys[slot]) & mask);
- /*! #end !*/
- /*! #end !*/
- }
- }
- }
-
- /**
- * Allocate internal buffers for a given capacity.
- *
- * @param capacity New capacity (must be a power of two).
- */
- @SuppressWarnings("boxing")
- private void allocateBuffers(final int capacity) {
- try {
-
- final KType[] keys = Intrinsics. newArray(capacity);
- final VType[] values = Intrinsics. newArray(capacity);
-
- /*! #if ($RH) !*/
- final int[] cached = new int[capacity];
- /*! #end !*/
-
- this.keys = keys;
- this.values = values;
-
- /*! #if ($RH) !*/
- this.hash_cache = cached;
- /*! #end !*/
-
- //allocate so that there is at least one slot that remains allocated = false
- //this is compulsory to guarantee proper stop in searching loops
- this.resizeAt = HashContainers.expandAtCount(capacity, this.loadFactor);
- } catch (final OutOfMemoryError e) {
-
- throw new BufferAllocationException(
- "Not enough memory to allocate buffers to grow from %d -> %d elements",
- e,
- (this.keys == null) ? 0 : this.keys.length,
- capacity);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public VType remove(final KType key) {
- if (Intrinsics. isEmpty(key)) {
-
- if (this.allocatedDefaultKey) {
-
- final VType previousValue = this.allocatedDefaultKeyValue;
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- //help the GC
- this.allocatedDefaultKeyValue = Intrinsics. empty();
- /*! #end !*/
-
- this.allocatedDefaultKey = false;
- return previousValue;
- }
-
- return this.defaultValue;
- }
-
- final int mask = this.keys.length - 1;
-
- final KType[] keys = Intrinsics. cast(this.keys);
-
- final KTypeHashingStrategy super KType> strategy = this.hashStrategy;
-
- int slot = REHASH(strategy, key) & mask;
- KType existing;
-
- /*! #if ($RH) !*/
- int dist = 0;
- final int[] cached = this.hash_cache;
- /*! #end !*/
-
- while (!Intrinsics. isEmpty(existing = keys[slot])
- /*! #if ($RH) !*/&& dist <= probe_distance(slot, cached) /*! #end !*/) {
-
- if (strategy.equals(key, existing)) {
-
- final VType value = Intrinsics. cast(this.values[slot]);
- shiftConflictingKeys(slot);
-
- return value;
- }
- slot = (slot + 1) & mask;
-
- /*! #if ($RH) !*/
- dist++;
- /*! #end !*/
- } //end while true
-
- return this.defaultValue;
- }
-
- /**
- * Shift all the slot-conflicting keys allocated to (and including) slot.
- */
- private void shiftConflictingKeys(int gapSlot) {
- final int mask = this.keys.length - 1;
-
- final KTypeHashingStrategy super KType> strategy = this.hashStrategy;
-
- final KType[] keys = Intrinsics. cast(this.keys);
- final VType[] values = Intrinsics. cast(this.values);
-
- /*! #if ($RH) !*/
- final int[] cached = this.hash_cache;
- /*! #else
- final int perturb = this.perturbation;
- #end !*/
-
- // Perform shifts of conflicting keys to fill in the gap.
- int distance = 0;
- while (true) {
-
- final int slot = (gapSlot + (++distance)) & mask;
-
- final KType existing = keys[slot];
- final VType existingValue = values[slot];
-
- if (Intrinsics. isEmpty(existing)) {
- break;
- }
-
- /*! #if ($RH) !*/
- //use the cached value, no need to recompute
- final int idealSlotModMask = cached[slot];
- /*! #if($DEBUG) !*/
- //Check invariants
- assert idealSlotModMask == (REHASH(strategy, existing) & mask);
- /*! #end !*/
- /*! #else
- final int idealSlotModMask = REHASH2(strategy, existing, perturb) & mask;
- #end !*/
-
- //original HPPC code: shift = (slot - idealSlot) & mask;
- //equivalent to shift = (slot & mask - idealSlot & mask) & mask;
- //since slot and idealSlotModMask are already folded, we have :
- final int shift = (slot - idealSlotModMask) & mask;
-
- if (shift >= distance) {
- // Entry at this position was originally at or before the gap slot.
- // Move the conflict-shifted entry to the gap's position and repeat the procedure
- // for any entries to the right of the current position, treating it
- // as the new gap.
- keys[gapSlot] = existing;
- values[gapSlot] = existingValue;
-
- /*! #if ($RH) !*/
- cached[gapSlot] = idealSlotModMask;
- /*! #end !*/
-
- gapSlot = slot;
- distance = 0;
- }
- } //end while
-
- // Mark the last found gap slot without a conflict as empty.
- keys[gapSlot] = Intrinsics. empty();
-
- /* #if ($TemplateOptions.VTypeGeneric) */
- values[gapSlot] = Intrinsics. empty();
- /* #end */
- this.assigned--;
- }
-
- /**
- * {@inheritDoc}
- */
- @SuppressWarnings("unchecked")
- @Override
- public int removeAll(final KTypeContainer super KType> other) {
- final int before = this.size();
-
- //1) other is a KTypeLookupContainer, so with fast lookup guarantees
- //and is bigger than this, so take advantage of both and iterate over this
- //and test other elements by their contains().
- if (other.size() >= before && other instanceof KTypeLookupContainer>) {
-
- if (this.allocatedDefaultKey) {
-
- if (other.contains(Intrinsics. empty())) {
- this.allocatedDefaultKey = false;
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- //help the GC
- this.allocatedDefaultKeyValue = Intrinsics. empty();
- /*! #end !*/
- }
- }
-
- final KType[] keys = Intrinsics. cast(this.keys);
-
- for (int i = 0; i < keys.length;) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i]) && other.contains(existing)) {
-
- shiftConflictingKeys(i);
- // Shift, do not increment slot.
- } else {
- i++;
- }
- }
- } else {
- //2) Do not use contains() from container, which may lead to O(n**2) execution times,
- //so it iterate linearly and call remove() from map which is O(1).
- for (final KTypeCursor super KType> c : other) {
-
- remove(Intrinsics. cast(c.value));
- }
- }
-
- return before - this.size();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int removeAll(final KTypePredicate super KType> predicate) {
- final int before = this.size();
-
- if (this.allocatedDefaultKey) {
-
- if (predicate.apply(Intrinsics. empty())) {
- this.allocatedDefaultKey = false;
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- //help the GC
- this.allocatedDefaultKeyValue = Intrinsics. empty();
- /*! #end !*/
- }
- }
-
- final KType[] keys = Intrinsics. cast(this.keys);
-
- for (int i = 0; i < keys.length;) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i]) && predicate.apply(existing)) {
-
- shiftConflictingKeys(i);
- // Shift, do not increment slot.
- } else {
- i++;
- }
- }
-
- return before - this.size();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int removeAll(final KTypeVTypePredicate super KType, ? super VType> predicate) {
-
- final int before = this.size();
-
- if (this.allocatedDefaultKey) {
-
- if (predicate.apply(Intrinsics. empty(), this.allocatedDefaultKeyValue)) {
- this.allocatedDefaultKey = false;
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- //help the GC
- this.allocatedDefaultKeyValue = Intrinsics. empty();
- /*! #end !*/
- }
- }
-
- final KType[] keys = Intrinsics. cast(this.keys);
- final VType[] values = Intrinsics. cast(this.values);
-
- for (int i = 0; i < keys.length;) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i]) && predicate.apply(existing, values[i])) {
-
- shiftConflictingKeys(i);
- // Shift, do not increment slot.
- } else {
- i++;
- }
- }
-
- return before - this.size();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public VType get(final KType key) {
- if (Intrinsics. isEmpty(key)) {
-
- if (this.allocatedDefaultKey) {
-
- return this.allocatedDefaultKeyValue;
- }
-
- return this.defaultValue;
- }
-
- final int mask = this.keys.length - 1;
-
- final KTypeHashingStrategy super KType> strategy = this.hashStrategy;
-
- final KType[] keys = Intrinsics. cast(this.keys);
-
- int slot = REHASH(strategy, key) & mask;
- KType existing;
-
- /*! #if ($RH) !*/
- int dist = 0;
- final int[] cached = this.hash_cache;
- /*! #end !*/
-
- while (!Intrinsics. isEmpty(existing = keys[slot])
- /*! #if ($RH) !*/&& dist <= probe_distance(slot, cached) /*! #end !*/) {
-
- if (strategy.equals(key, existing)) {
-
- return Intrinsics. cast(this.values[slot]);
- }
- slot = (slot + 1) & mask;
-
- /*! #if ($RH) !*/
- dist++;
- /*! #end !*/
- } //end while true
-
- return this.defaultValue;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean containsKey(final KType key) {
- if (Intrinsics. isEmpty(key)) {
-
- return this.allocatedDefaultKey;
- }
-
- final int mask = this.keys.length - 1;
-
- final KTypeHashingStrategy super KType> strategy = this.hashStrategy;
-
- final KType[] keys = Intrinsics. cast(this.keys);
-
- int slot = REHASH(strategy, key) & mask;
- KType existing;
-
- /*! #if ($RH) !*/
- int dist = 0;
- final int[] cached = this.hash_cache;
- /*! #end !*/
-
- while (!Intrinsics. isEmpty(existing = keys[slot])
- /*! #if ($RH) !*/&& dist <= probe_distance(slot, cached) /*! #end !*/) {
-
- if (strategy.equals(key, existing)) {
- return true;
- }
- slot = (slot + 1) & mask;
-
- /*! #if ($RH) !*/
- dist++;
- /*! #end !*/
- } //end while true
-
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void clear() {
- this.assigned = 0;
-
- // States are always cleared.
- this.allocatedDefaultKey = false;
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- //help the GC
- this.allocatedDefaultKeyValue = Intrinsics. empty();
- /*! #end !*/
-
- //Faster than Arrays.fill(keys, null); // Help the GC.
- KTypeArrays.blankArray(this.keys, 0, this.keys.length);
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- //Faster than Arrays.fill(values, null); // Help the GC.
- VTypeArrays. blankArray(Intrinsics. cast(this.values), 0, this.values.length);
- /*! #end !*/
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int size() {
- return this.assigned + (this.allocatedDefaultKey ? 1 : 0);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int capacity() {
-
- return this.resizeAt;
- }
-
- /**
- * {@inheritDoc}
- *
- *
Note that an empty container may still contain many deleted keys (that occupy buffer
- * space). Adding even a single element to such a container may cause rehashing.
- */
- @Override
- public boolean isEmpty() {
- return size() == 0;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- final KTypeHashingStrategy super KType> strategy = this.hashStrategy;
-
- int h = 0;
-
- if (this.allocatedDefaultKey) {
- h += BitMixer.mix(this.allocatedDefaultKeyValue);
- }
-
- final KType[] keys = Intrinsics. cast(this.keys);
- final VType[] values = Intrinsics. cast(this.values);
-
- for (int i = keys.length; --i >= 0;) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i])) {
-
- h += BitMixer.mix(strategy.computeHashCode(existing)) ^ BitMixer.mix(values[i]);
- }
- }
-
- return h;
- }
-
- /**
- * {@inheritDoc}
- */
- @SuppressWarnings("unchecked")
- @Override
- public boolean equals(final Object obj) {
-
- if (obj != null) {
- if (obj == this) {
- return true;
- }
-
- //must be of the same class, subclasses are not comparable
- if (obj.getClass() != this.getClass()) {
- return false;
- }
-
- //their hash strategies MUST be "equal", i.e apply the same equivalence criteria.
- if (!this.hashStrategy.equals(((KTypeVTypeCustomHashMap) obj).hashStrategy)) {
- return false;
- }
-
- final KTypeVTypeCustomHashMap other = (KTypeVTypeCustomHashMap) obj;
-
- //must be of the same size
- if (other.size() != this.size()) {
- return false;
- }
-
- final EntryIterator it = this.iterator();
-
- while (it.hasNext()) {
- final KTypeVTypeCursor c = it.next();
-
- if (!other.containsKey(c.key)) {
- //recycle
- it.release();
- return false;
- }
-
- final VType otherValue = other.get(c.key);
-
- if (!Intrinsics. equals(c.value, otherValue)) {
- //recycle
- it.release();
- return false;
- }
- } //end while
- return true;
- }
- return false;
- }
-
- /**
- * An iterator implementation for {@link #iterator}.
- * Holds a KTypeVTypeCursor returning
- * (key, value, index) = (KType key, VType value, index the position in keys {@link KTypeVTypeCustomHashMap#keys}, or keys.length for key = 0/null)
- */
- public final class EntryIterator extends AbstractIterator>
- {
- public final KTypeVTypeCursor cursor;
-
- public EntryIterator() {
- this.cursor = new KTypeVTypeCursor();
- this.cursor.index = -2;
- }
-
- /**
- * Iterate backwards w.r.t the buffer, to
- * minimize collision chains when filling another hash container (ex. with putAll())
- */
- @Override
- protected KTypeVTypeCursor fetch() {
- if (this.cursor.index == KTypeVTypeCustomHashMap.this.keys.length + 1) {
-
- if (KTypeVTypeCustomHashMap.this.allocatedDefaultKey) {
-
- this.cursor.index = KTypeVTypeCustomHashMap.this.keys.length;
- this.cursor.key = Intrinsics. empty();
- this.cursor.value = KTypeVTypeCustomHashMap.this.allocatedDefaultKeyValue;
-
- return this.cursor;
-
- }
- //no value associated with the default key, continue iteration...
- this.cursor.index = KTypeVTypeCustomHashMap.this.keys.length;
- }
-
- int i = this.cursor.index - 1;
-
- while (i >= 0 && !is_allocated(i, Intrinsics. cast(KTypeVTypeCustomHashMap.this.keys))) {
- i--;
- }
-
- if (i == -1) {
- return done();
- }
-
- this.cursor.index = i;
- this.cursor.key = Intrinsics. cast(KTypeVTypeCustomHashMap.this.keys[i]);
- this.cursor.value = Intrinsics. cast(KTypeVTypeCustomHashMap.this.values[i]);
-
- return this.cursor;
- }
- }
-
- /**
- * internal pool of EntryIterator
- */
- protected final IteratorPool, EntryIterator> entryIteratorPool = new IteratorPool, EntryIterator>(
- new ObjectFactory() {
-
- @Override
- public EntryIterator create() {
- return new EntryIterator();
- }
-
- @Override
- public void initialize(final EntryIterator obj) {
- obj.cursor.index = KTypeVTypeCustomHashMap.this.keys.length + 1;
- }
-
- @Override
- public void reset(final EntryIterator obj) {
- /*! #if ($TemplateOptions.KTypeGeneric) !*/
- obj.cursor.key = null;
- /*! #end !*/
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- obj.cursor.value = null;
- /*! #end !*/
- }
- });
-
- /**
- * {@inheritDoc}
- */
- @Override
- public EntryIterator iterator() {
- //return new EntryIterator();
- return this.entryIteratorPool.borrow();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public > T forEach(final T procedure) {
- if (this.allocatedDefaultKey) {
-
- procedure.apply(Intrinsics. empty(), this.allocatedDefaultKeyValue);
- }
-
- final KType[] keys = Intrinsics. cast(this.keys);
- final VType[] values = Intrinsics. cast(this.values);
-
- //Iterate in reverse for side-stepping the longest conflict chain
- //in another hash, in case apply() is actually used to fill another hash container.
- for (int i = keys.length - 1; i >= 0; i--) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i])) {
- procedure.apply(existing, values[i]);
- }
- }
-
- return procedure;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public > T forEach(final T predicate) {
- if (this.allocatedDefaultKey) {
-
- if (!predicate.apply(Intrinsics. empty(), this.allocatedDefaultKeyValue)) {
-
- return predicate;
- }
- }
-
- final KType[] keys = Intrinsics. cast(this.keys);
- final VType[] values = Intrinsics. cast(this.values);
-
- //Iterate in reverse for side-stepping the longest conflict chain
- //in another hash, in case apply() is actually used to fill another hash container.
- for (int i = keys.length - 1; i >= 0; i--) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i])) {
- if (!predicate.apply(existing, values[i])) {
- break;
- }
- }
- } //end for
-
- return predicate;
- }
-
- /**
- * {@inheritDoc}
- * @return a new KeysCollection view of the keys of this map.
- */
- @Override
- public KeysCollection keys() {
- return new KeysCollection();
- }
-
- /**
- * A view of the keys inside this map.
- */
- public final class KeysCollection extends AbstractKTypeCollection implements KTypeLookupContainer
- {
- private final KTypeVTypeCustomHashMap owner = KTypeVTypeCustomHashMap.this;
-
- @Override
- public boolean contains(final KType e) {
- return containsKey(e);
- }
-
- @Override
- public > T forEach(final T procedure) {
-
- if (this.owner.allocatedDefaultKey) {
-
- procedure.apply(Intrinsics. empty());
- }
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
-
- //Iterate in reverse for side-stepping the longest conflict chain
- //in another hash, in case apply() is actually used to fill another hash container.
- for (int i = keys.length - 1; i >= 0; i--) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i])) {
- procedure.apply(existing);
- }
- }
-
- return procedure;
- }
-
- @Override
- public > T forEach(final T predicate) {
- if (this.owner.allocatedDefaultKey) {
-
- if (!predicate.apply(Intrinsics. empty())) {
-
- return predicate;
- }
- }
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
-
- //Iterate in reverse for side-stepping the longest conflict chain
- //in another hash, in case apply() is actually used to fill another hash container.
- for (int i = keys.length - 1; i >= 0; i--) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i])) {
- if (!predicate.apply(existing)) {
- break;
- }
- }
- }
-
- return predicate;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public KeysIterator iterator() {
- //return new KeysIterator();
- return this.keyIteratorPool.borrow();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int size() {
- return this.owner.size();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int capacity() {
-
- return this.owner.capacity();
- }
-
- @Override
- public void clear() {
- this.owner.clear();
- }
-
- @Override
- public int removeAll(final KTypePredicate super KType> predicate) {
- return this.owner.removeAll(predicate);
- }
-
- @Override
- public int removeAll(final KType e) {
- final boolean hasKey = this.owner.containsKey(e);
- int result = 0;
- if (hasKey) {
- this.owner.remove(e);
- result = 1;
- }
- return result;
- }
-
- /**
- * internal pool of KeysIterator
- */
- protected final IteratorPool, KeysIterator> keyIteratorPool = new IteratorPool, KeysIterator>(
- new ObjectFactory() {
-
- @Override
- public KeysIterator create() {
- return new KeysIterator();
- }
-
- @Override
- public void initialize(final KeysIterator obj) {
- obj.cursor.index = KTypeVTypeCustomHashMap.this.keys.length + 1;
- }
-
- @Override
- public void reset(final KeysIterator obj) {
- /*! #if ($TemplateOptions.KTypeGeneric) !*/
- obj.cursor.value = null;
- /*! #end !*/
-
- }
- });
-
- @Override
- public KType[] toArray(final KType[] target) {
- int count = 0;
-
- if (this.owner.allocatedDefaultKey) {
-
- target[count++] = Intrinsics. empty();
- }
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
-
- for (int i = 0; i < keys.length; i++) {
- KType existing;
- if (!Intrinsics. isEmpty(existing = keys[i])) {
- target[count++] = existing;
- }
- }
-
- assert count == this.owner.size();
- return target;
- }
- };
-
- /**
- * An iterator over the set of keys.
- * Holds a KTypeCursor returning (value, index) = (KType key, index the position in buffer {@link KTypeVTypeCustomHashMap#keys}, or keys.length for key = 0/null.)
- */
- public final class KeysIterator extends AbstractIterator>
- {
- public final KTypeCursor cursor;
-
- public KeysIterator() {
- this.cursor = new KTypeCursor();
- this.cursor.index = -2;
- }
-
- /**
- * Iterate backwards w.r.t the buffer, to
- * minimize collision chains when filling another hash container (ex. with putAll())
- */
- @Override
- protected KTypeCursor fetch() {
-
- if (this.cursor.index == KTypeVTypeCustomHashMap.this.keys.length + 1) {
-
- if (KTypeVTypeCustomHashMap.this.allocatedDefaultKey) {
-
- this.cursor.index = KTypeVTypeCustomHashMap.this.keys.length;
- this.cursor.value = Intrinsics. empty();
-
- return this.cursor;
-
- }
- //no value associated with the default key, continue iteration...
- this.cursor.index = KTypeVTypeCustomHashMap.this.keys.length;
- }
-
- int i = this.cursor.index - 1;
-
- while (i >= 0 && !is_allocated(i, Intrinsics. cast(KTypeVTypeCustomHashMap.this.keys))) {
- i--;
- }
-
- if (i == -1) {
- return done();
- }
-
- this.cursor.index = i;
- this.cursor.value = Intrinsics. cast(KTypeVTypeCustomHashMap.this.keys[i]);
-
- return this.cursor;
- }
- }
-
- /**
- * {@inheritDoc}
- * @return a new ValuesCollection view of the values of this map.
- */
- @Override
- public ValuesCollection values() {
- return new ValuesCollection();
- }
-
- /**
- * A view over the set of values of this map.
- */
- public final class ValuesCollection extends AbstractKTypeCollection
- {
- private final KTypeVTypeCustomHashMap owner = KTypeVTypeCustomHashMap.this;
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int size() {
- return this.owner.size();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int capacity() {
-
- return this.owner.capacity();
- }
-
- @Override
- public boolean contains(final VType value) {
- if (this.owner.allocatedDefaultKey && Intrinsics. equals(value, this.owner.allocatedDefaultKeyValue)) {
-
- return true;
- }
-
- // This is a linear scan over the values, but it's in the contract, so be it.
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
- final VType[] values = Intrinsics. cast(this.owner.values);
-
- for (int slot = 0; slot < keys.length; slot++) {
- if (is_allocated(slot, keys) && Intrinsics. equals(value, values[slot])) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public > T forEach(final T procedure) {
- if (this.owner.allocatedDefaultKey) {
-
- procedure.apply(this.owner.allocatedDefaultKeyValue);
- }
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
- final VType[] values = Intrinsics. cast(this.owner.values);
-
- for (int slot = 0; slot < keys.length; slot++) {
- if (is_allocated(slot, keys)) {
- procedure.apply(values[slot]);
- }
- }
-
- return procedure;
- }
-
- @Override
- public > T forEach(final T predicate) {
- if (this.owner.allocatedDefaultKey) {
-
- if (!predicate.apply(this.owner.allocatedDefaultKeyValue)) {
- return predicate;
- }
- }
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
- final VType[] values = Intrinsics. cast(this.owner.values);
-
- for (int slot = 0; slot < keys.length; slot++) {
- if (is_allocated(slot, keys)) {
- if (!predicate.apply(values[slot])) {
- break;
- }
- }
- }
-
- return predicate;
- }
-
- @Override
- public ValuesIterator iterator() {
- // return new ValuesIterator();
- return this.valuesIteratorPool.borrow();
- }
-
- /**
- * {@inheritDoc}
- * Indeed removes all the (key,value) pairs matching
- * (key ? , e) with the same e, from the map.
- */
- @Override
- public int removeAll(final VType e) {
- final int before = this.owner.size();
-
- if (this.owner.allocatedDefaultKey) {
-
- if (Intrinsics. equals(e, this.owner.allocatedDefaultKeyValue)) {
-
- this.owner.allocatedDefaultKey = false;
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- //help the GC
- this.owner.allocatedDefaultKeyValue = Intrinsics. empty();
- /*! #end !*/
- }
- }
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
- final VType[] values = Intrinsics. cast(this.owner.values);
-
- for (int slot = 0; slot < keys.length;) {
- if (is_allocated(slot, keys) && Intrinsics. equals(e, values[slot])) {
-
- shiftConflictingKeys(slot);
- // Shift, do not increment slot.
- } else {
- slot++;
- }
- }
- return before - this.owner.size();
- }
-
- /**
- * {@inheritDoc} Indeed removes all the (key,value) pairs matching the
- * predicate for the values, from the map.
- */
- @Override
- public int removeAll(final KTypePredicate super VType> predicate) {
- final int before = this.owner.size();
-
- if (this.owner.allocatedDefaultKey) {
-
- if (predicate.apply(this.owner.allocatedDefaultKeyValue)) {
-
- this.owner.allocatedDefaultKey = false;
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- //help the GC
- this.owner.allocatedDefaultKeyValue = Intrinsics. empty();
- /*! #end !*/
- }
- }
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
- final VType[] values = Intrinsics. cast(this.owner.values);
-
- for (int slot = 0; slot < keys.length;) {
- if (is_allocated(slot, keys) && predicate.apply(values[slot])) {
-
- shiftConflictingKeys(slot);
- // Shift, do not increment slot.
- } else {
- slot++;
- }
- }
-
- return before - this.owner.size();
- }
-
- /**
- * {@inheritDoc}
- * Alias for clear() the whole map.
- */
- @Override
- public void clear() {
- this.owner.clear();
- }
-
- /**
- * internal pool of ValuesIterator
- */
- protected final IteratorPool, ValuesIterator> valuesIteratorPool = new IteratorPool, ValuesIterator>(
- new ObjectFactory() {
-
- @Override
- public ValuesIterator create() {
-
- return new ValuesIterator();
- }
-
- @Override
- public void initialize(final ValuesIterator obj) {
- obj.cursor.index = KTypeVTypeCustomHashMap.this.keys.length + 1;
- }
-
- @Override
- public void reset(final ValuesIterator obj) {
-
- /*! #if ($TemplateOptions.VTypeGeneric) !*/
- obj.cursor.value = null;
- /*! #end !*/
-
- }
- });
-
- @Override
- public VType[] toArray(final VType[] target) {
- int count = 0;
-
- if (this.owner.allocatedDefaultKey) {
-
- target[count++] = this.owner.allocatedDefaultKeyValue;
- }
-
- final KType[] keys = Intrinsics. cast(this.owner.keys);
- final VType[] values = Intrinsics. cast(this.owner.values);
-
- for (int i = 0; i < values.length; i++) {
- if (is_allocated(i, keys)) {
- target[count++] = values[i];
- }
- }
-
- assert count == this.owner.size();
- return target;
- }
- }
-
- /**
- * An iterator over the set of values.
- * Holds a KTypeCursor returning (value, index) = (VType value, index the position in buffer {@link KTypeVTypeCustomHashMap#values},
- * or values.length for value = {@link KTypeVTypeCustomHashMap#allocatedDefaultKeyValue}).
- */
- public final class ValuesIterator extends AbstractIterator>
- {
- public final KTypeCursor cursor;
-
- public ValuesIterator() {
- this.cursor = new KTypeCursor();
- this.cursor.index = -2;
- }
-
- /**
- * Iterate backwards w.r.t the buffer, to
- * minimize collision chains when filling another hash container (ex. with putAll())
- */
- @Override
- protected KTypeCursor fetch() {
-
- if (this.cursor.index == KTypeVTypeCustomHashMap.this.values.length + 1) {
-
- if (KTypeVTypeCustomHashMap.this.allocatedDefaultKey) {
-
- this.cursor.index = KTypeVTypeCustomHashMap.this.values.length;
- this.cursor.value = KTypeVTypeCustomHashMap.this.allocatedDefaultKeyValue;
-
- return this.cursor;
-
- }
- //no value associated with the default key, continue iteration...
- this.cursor.index = KTypeVTypeCustomHashMap.this.keys.length;
- }
-
- int i = this.cursor.index - 1;
-
- while (i >= 0 && !is_allocated(i, Intrinsics. cast(KTypeVTypeCustomHashMap.this.keys))) {
- i--;
- }
-
- if (i == -1) {
- return done();
- }
-
- this.cursor.index = i;
- this.cursor.value = Intrinsics. cast(KTypeVTypeCustomHashMap.this.values[i]);
-
- return this.cursor;
- }
- }
-
- /**
- * {@inheritDoc}
- * #if ($TemplateOptions.AnyGeneric)
- * The returned clone will use the same HashingStrategy strategy.
- * #end
- */
- @Override
- public KTypeVTypeCustomHashMap clone() {
- //clone to size to prevent exponential growth
- final KTypeVTypeCustomHashMap cloned = new KTypeVTypeCustomHashMap(this.size(),
- this.loadFactor, this.hashStrategy);
-
- //We must NOT clone because of independent perturbations seeds
- cloned.putAll(this);
-
- cloned.allocatedDefaultKeyValue = this.allocatedDefaultKeyValue;
- cloned.allocatedDefaultKey = this.allocatedDefaultKey;
- cloned.defaultValue = this.defaultValue;
-
- return cloned;
-
- }
-
- /**
- * Convert the contents of this map to a human-friendly string.
- */
- @Override
- public String toString() {
- final StringBuilder buffer = new StringBuilder();
- buffer.append("[");
-
- boolean first = true;
- for (final KTypeVTypeCursor cursor : this) {
- if (!first) {
- buffer.append(", ");
- }
- buffer.append(cursor.key);
- buffer.append("=>");
- buffer.append(cursor.value);
- first = false;
- }
- buffer.append("]");
- return buffer.toString();
- }
-
- /**
- * Creates a hash map from two index-aligned arrays of key-value pairs. Default load factor is used.
- */
- public static KTypeVTypeCustomHashMap from(final KType[] keys, final VType[] values,
- final KTypeHashingStrategy super KType> hashStrategy) {
- if (keys.length != values.length) {
- throw new IllegalArgumentException("Arrays of keys and values must have an identical length.");
- }
-
- final KTypeVTypeCustomHashMap map = new KTypeVTypeCustomHashMap(keys.length,
- hashStrategy);
-
- for (int i = 0; i < keys.length; i++) {
- map.put(keys[i], values[i]);
- }
- return map;
- }
-
- /**
- * Create a hash map from another associative container. Default load factor is used.
- */
- public static KTypeVTypeCustomHashMap from(
- final KTypeVTypeAssociativeContainer container,
- final KTypeHashingStrategy super KType> hashStrategy) {
- return new KTypeVTypeCustomHashMap(container, hashStrategy);
- }
-
- /**
- * Create a new hash map without providing the full generic signature (constructor
- * shortcut).
- */
- public static KTypeVTypeCustomHashMap newInstance(
- final KTypeHashingStrategy super KType> hashStrategy) {
- return new KTypeVTypeCustomHashMap(hashStrategy);
- }
-
- /**
- * Create a new hash map with initial capacity and load factor control. (constructor
- * shortcut).
- */
- public static KTypeVTypeCustomHashMap newInstance(final int initialCapacity,
- final double loadFactor, final KTypeHashingStrategy super KType> hashStrategy) {
- return new KTypeVTypeCustomHashMap(initialCapacity, loadFactor, hashStrategy);
- }
-
- /**
- * Return the current {@link KTypeHashingStrategy} in use.
- */
- public KTypeHashingStrategy super KType> strategy() {
- return this.hashStrategy;
- }
-
- /**
- * Returns the "default value" value used
- * in containers methods returning "default value"
- */
- @Override
- public VType getDefaultValue() {
- return this.defaultValue;
- }
-
- /**
- * Set the "default value" value to be used
- * in containers methods returning "default value"
- */
- @Override
- public void setDefaultValue(final VType defaultValue) {
- this.defaultValue = defaultValue;
- }
-
- //Test for existence in template
- /*! #if ($TemplateOptions.declareInline("is_allocated(slot, keys)",
- "<*,*>==>!Intrinsics.isEmpty(keys[slot])")) !*/
- /**
- * template version
- * (actual method is inlined in generated code)
- */
- private boolean is_allocated(final int slot, final KType[] keys) {
-
- return !Intrinsics. isEmpty(keys[slot]);
- }
-
- /*! #end !*/
-
- /*! #if ($TemplateOptions.declareInline("probe_distance(slot, cache)",
- "<*,*>==>slot < cache[slot] ? slot + cache.length - cache[slot] : slot - cache[slot]")) !*/
- /**
- * (actual method is inlined in generated code)
- */
- private int probe_distance(final int slot, final int[] cache) {
-
- final int rh = cache[slot];
-
- /*! #if($DEBUG) !*/
- //Check : cached hashed slot is == computed value
- final int mask = cache.length - 1;
- assert rh == (REHASH(this.hashStrategy, Intrinsics. cast(this.keys[slot])) & mask);
- /*! #end !*/
-
- if (slot < rh) {
- //wrap around
- return slot + cache.length - rh;
- }
-
- return slot - rh;
- }
-
- /*! #end !*/
-
- /*! #if ($TemplateOptions.declareInline("REHASH(strategy, value)",
- "<*,*>==>BitMixer.mix(strategy.computeHashCode(value) , this.perturbation )")) !*/
- /**
- * (actual method is inlined in generated code)
- */
- private int REHASH(final KTypeHashingStrategy super KType> strategy, final KType value) {
-
- return BitMixer.mix(strategy.computeHashCode(value), this.perturbation);
- }
-
- /*! #end !*/
-
- /*! #if ($TemplateOptions.declareInline("REHASH2(strategy, value, perturb)",
- "<*,*>==>BitMixer.mix(strategy.computeHashCode(value) , perturb)")) !*/
- /**
- * REHASH2 method for rehashing the keys with perturbation seed as parameter
- * (inlined in generated code)
- * Thanks to single array mode, no need to check for null/0 or booleans.
- */
- private int REHASH2(final KTypeHashingStrategy super KType> strategy, final KType value, final int perturb) {
-
- return BitMixer.mix(strategy.computeHashCode(value), perturb);
- }
- /*! #end !*/
-}
diff --git a/hppcrt/src/main/templates/com/carrotsearch/hppcrt/maps/KTypeVTypeHashMap.java b/hppcrt/src/main/templates/com/carrotsearch/hppcrt/maps/KTypeVTypeHashMap.java
index d5222850a..8403a5af9 100644
--- a/hppcrt/src/main/templates/com/carrotsearch/hppcrt/maps/KTypeVTypeHashMap.java
+++ b/hppcrt/src/main/templates/com/carrotsearch/hppcrt/maps/KTypeVTypeHashMap.java
@@ -16,6 +16,13 @@
* A hash map of KType to VType, implemented using open
* addressing with linear probing for collision resolution.
*
+#if ($TemplateOptions.KTypeGeneric)
+ *
In addition, the hashing strategy can be changed
+ * by overriding ({@link #equalKeys(Object, Object)} and {@link #hashKey(Object)}) together,
+ * which then replaces the usual ({@link #equals(Object)} and {@link #hashCode()}) from the keys themselves.
+ * This is useful to define the equivalence of keys when the user has no control over the keys implementation.
+ *
+#end
*
* The internal buffers of this implementation ({@link #keys}, {@link #values}),
* are always allocated to the nearest size that is a power of two. When
@@ -121,6 +128,45 @@ public class KTypeVTypeHashMap
*/
private final int perturbation = Containers.randomSeed32();
+ /*! #if ($TemplateOptions.KTypeGeneric) !*/
+
+ /**
+ * Override this method, together with {@link #equalKeys(Object, Object)}
+ * to customize the hashing strategy. Note that this method is guaranteed
+ * to be called with a non-null key argument.
+ * By default, this method calls key.{@link #hashCode()}.
+ * @param key KType to be hashed.
+ * @return the hashed value of key, following the same semantic
+ * as {@link #hashCode()};
+ * @see #hashCode()
+ * @see #equalKeys(Object, Object)
+ */
+ protected int hashKey(final KType key) {
+
+ //default maps on Object.hashCode()
+ return key.hashCode();
+ }
+
+ /**
+ * Override this method together with {@link #hashKey(Object)}
+ * to customize the hashing strategy. Note that this method is guaranteed
+ * to be called with both non-null arguments.
+ * By default, this method calls a.{@link #equals(b)}.
+ * @param a not-null KType to be compared
+ * @param b not-null KType to be compared
+ * @return true if a and b are considered equal, following the same
+ * semantic as {@link #equals(Object)}.
+ * @see #equals(Object)
+ * @see #hashKey(Object)
+ */
+ protected boolean equalKeys(final KType a, final KType b) {
+
+ //default maps on Object.equals()
+ return Intrinsics. equalsNotNull(a, b);
+ }
+
+ /*! #end !*/
+
/**
* Default constructor: Creates a hash map with the default capacity of {@link Containers#DEFAULT_EXPECTED_ELEMENTS},
* load factor of {@link HashContainers#DEFAULT_LOAD_FACTOR}.
@@ -208,7 +254,7 @@ public VType put(KType key, VType value) {
while (!Intrinsics. isEmpty(existing = keys[slot])) {
- if (Intrinsics. equalsNotNull(key, existing)) {
+ if (KEYEQUALS(key, existing)) {
final VType oldValue = Intrinsics. cast(this.values[slot]);
values[slot] = value;
@@ -546,7 +592,7 @@ public VType remove(final KType key) {
while (!Intrinsics. isEmpty(existing = keys[slot])
/*! #if ($RH) !*/&& dist <= probe_distance(slot, cached) /*! #end !*/) {
- if (Intrinsics. equalsNotNull(key, existing)) {
+ if (KEYEQUALS(key, existing)) {
final VType value = Intrinsics. cast(this.values[slot]);
@@ -786,7 +832,7 @@ public VType get(final KType key) {
while (!Intrinsics. isEmpty(existing = keys[slot])
/*! #if ($RH) !*/&& dist <= probe_distance(slot, cached) /*! #end !*/) {
- if (Intrinsics. equalsNotNull(key, existing)) {
+ if (KEYEQUALS(key, existing)) {
return Intrinsics. cast(this.values[slot]);
}
@@ -826,7 +872,7 @@ public boolean containsKey(final KType key) {
while (!Intrinsics. isEmpty(existing = keys[slot])
/*! #if ($RH) !*/&& dist <= probe_distance(slot, cached) /*! #end !*/) {
- if (Intrinsics. equalsNotNull(key, existing)) {
+ if (KEYEQUALS(key, existing)) {
return true;
}
slot = (slot + 1) & mask;
@@ -1753,7 +1799,7 @@ private int probe_distance(final int slot, final int[] cache) {
/*! #end !*/
/*! #if ($TemplateOptions.declareInline("REHASH(value)",
- "