All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.carrotsearch.hppcrt.maps.ObjectDoubleHashMap Maven / Gradle / Ivy

Go to download

High Performance Primitive Collections Realtime (fork of HPPC from Carrotsearch) Fundamental data structures (maps, sets, lists, queues, heaps, sorts) generated for combinations of object and primitive types to conserve JVM memory and speed up execution. The Realtime fork intends to extend the existing collections, by tweaking to remove any dynamic allocations at runtime, and to obtain low variance execution times whatever the input nature.

The newest version!
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.hash.*;

  
  
  
// If RH is defined, RobinHood Hashing is in effect :
  

/**
 * A hash map of Object to double, implemented using open
 * addressing with linear probing for collision resolution.
 *
 * 

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. *

*

* 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. *

* *

This implementation supports null keys.

* *

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. * * * * *

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,

*

- MoonPolySoft/Cliff Moon for the initial Robin-hood on HPPC implementation,

*

- Vincent Sonnier for the present implementation using cached hashes.

* */ @javax.annotation.Generated( date = "2017-07-11T19:16:27+0200", value = "KTypeVTypeHashMap.java") public class ObjectDoubleHashMap implements ObjectDoubleMap, Cloneable { protected double defaultValue = (0.0); /** * Hash-indexed array holding all keys. *

* 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 Object[] keys; /** * Hash-indexed array holding all values associated to the keys. * stored in {@link #keys}. */ public double [] values; /** * * Caches the hash value = hash(keys[i]) & mask, if keys[i] != 0/null, * for every index i. * * @see #assigned */ protected int[] hash_cache; /** * 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 double 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(); /** * 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 Object 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 Object to be compared * @param b not-null Object 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 ((a).equals((b))); } /** * Default constructor: Creates a hash map with the default capacity of {@link Containers#DEFAULT_EXPECTED_ELEMENTS}, * load factor of {@link HashContainers#DEFAULT_LOAD_FACTOR}. * *

See class notes about hash distribution importance.

*/ public ObjectDoubleHashMap() { this(Containers.DEFAULT_EXPECTED_ELEMENTS); } /** * Creates a hash map with the given initial capacity, default load factor of * {@link HashContainers#DEFAULT_LOAD_FACTOR}. * *

See class notes about hash distribution importance.

* * @param initialCapacity Initial capacity (greater than zero and automatically * rounded to the next power of two). */ public ObjectDoubleHashMap(final int initialCapacity) { this(initialCapacity, HashContainers.DEFAULT_LOAD_FACTOR); } /** * Creates a hash map with the given initial capacity, * load factor. * * @param loadFactor The load factor (greater than zero and smaller than 1). */ public ObjectDoubleHashMap(final int initialCapacity, final double loadFactor) { this.loadFactor = loadFactor; //take into account of the load factor to guarantee no reallocations before reaching initialCapacity. allocateBuffers(HashContainers.minBufferSize(initialCapacity, loadFactor)); } /** * Create a hash map from all key-value pairs of another container. */ public ObjectDoubleHashMap(final ObjectDoubleAssociativeContainer container) { this(container.size()); putAll(container); } /** * {@inheritDoc} */ @Override public double put(KType key, double value) { if (((key) == null)) { if (this.allocatedDefaultKey) { final double 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 KType[] keys = ((KType[])(this.keys)); int slot = (BitMixer.mix(hashKey((key)) , this.perturbation)) & mask; KType existing; final double[] values = ((this.values)); final int[] cached = this.hash_cache; KType tmpKey; double tmpValue; int tmpAllocated; int initial_slot = slot; int dist = 0; int existing_distance = 0; while (!((existing = keys[slot]) == null)) { existing_distance = probe_distance(slot, cached); //When first entering the while loop, then key == original key to search. //So either: //1) key is immediately found and the routine bail out, //or //2) If the Robin-hood criteria of distance is not met, we search the next slot, (usual linear probing) //or //3) else the criteria of distance is met, then (key,value) is swapped with the ones in //slot position which becomes the new (key,value) to consider. This is OK because keys are swapped only if dist > existing_distance, //i.e only if the key to add is NOT in the map, see containsKey(). So we steal the rich (a previously entered key, favored because having being inserted //in a less crowed array) to give to the poor, the now inserted key. Then, we start searching again in the next slot. // Robin-hood shortcut: if key exists, it can only be found in dist <= existing_distance range. //indeed we should expect to never see an existing element with a shorter probe count (existing_distance) //than our current count (dist): if that had happened, there would’ve been a swap during insertion, see below. //also see containsKey(), get() and remove() for the same trick. if ( dist <= existing_distance && (equalKeys((key), (existing)))) { final double oldValue = ((this.values[slot])); this.values[slot] = value; return oldValue; } //re-shuffle keys to minimize variance //we actually enter here only if the key to add is NOT in the map. 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; dist = existing_distance; } slot = (slot + 1) & mask; dist++; } //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++; cached[slot] = initial_slot; keys[slot] = key; values[slot] = value; } return this.defaultValue; } /** * {@inheritDoc} */ @Override public int putAll(final ObjectDoubleAssociativeContainer container) { return putAll((Iterable>) container); } /** * {@inheritDoc} */ @Override public int putAll(final Iterable> iterable) { final int count = this.size(); for (final ObjectDoubleCursor c : iterable) { put(c.key, c.value); } return this.size() - count; } /** * {@inheritDoc} */ @Override public boolean putIfAbsent(final KType key, final double value) { if (!containsKey(key)) { put(key, value); return true; } return false; } /** * 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 double putOrAdd(final KType key, double putValue, final double incrementValue) { if (containsKey(key)) { putValue = get(key); putValue = (double) (((putValue) + (incrementValue))); } put(key, putValue); return putValue; } /** * 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 double addTo(final KType key, final double incrementValue) { return putOrAdd(key, incrementValue, incrementValue); } /** * Expand the internal storage buffers (capacity) and rehash. */ private void expandAndPut(final KType pendingKey, final double pendingValue, final int freeSlot) { assert this.assigned == this.resizeAt; //default sentinel value is never in the keys[] array, so never trigger reallocs assert !((pendingKey) == null); // 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 = ((KType[])(this.keys)); final double[] oldValues = ((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; KType key = (null); double value = (0.0); int slot = -1; final KType[] keys = ((KType[])(this.keys)); final double[] values = ((this.values)); final int[] cached = this.hash_cache; KType tmpKey = (null); double tmpValue = (0.0); int tmpAllocated = -1; int initial_slot = -1; int dist = -1; int existing_distance = -1; //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;) { //only consider non-empty slots, of course if (!((key = oldKeys[i]) == null)) { value = oldValues[i]; slot = (BitMixer.mix(hashKey((key)) , (perturb))) & mask; initial_slot = slot; dist = 0; //similar to put(), except all inserted keys are known to be unique. while ((!(((keys)[(slot)]) == null))) { //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; dist = existing_distance; } slot = (slot + 1) & mask; dist++; } //end while cached[slot] = initial_slot; keys[slot] = key; values[slot] = value; } } } /** * 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 = ((KType[])new Object[(capacity)]); final double[] values = (new double[(capacity)]); final int[] cached = new int[capacity]; this.keys = keys; this.values = values; this.hash_cache = cached; //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 double remove(final KType key) { if (((key) == null)) { if (this.allocatedDefaultKey) { final double previousValue = this.allocatedDefaultKeyValue; this.allocatedDefaultKey = false; return previousValue; } return this.defaultValue; } final int mask = this.keys.length - 1; final KType[] keys = ((KType[])(this.keys)); int slot = (BitMixer.mix(hashKey((key)) , this.perturbation)) & mask; KType existing; int dist = 0; final int[] cached = this.hash_cache; while (!((existing = keys[slot]) == null) && dist <= probe_distance(slot, cached) ) { if ((equalKeys((key), (existing)))) { final double value = ((this.values[slot])); shiftConflictingKeys(slot); return value; } slot = (slot + 1) & mask; dist++; } //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 KType[] keys = ((KType[])(this.keys)); final double[] values = ((this.values)); final int[] cached = this.hash_cache; // 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 double existingValue = values[slot]; if (((existing) == null)) { break; } //use the cached value, no need to recompute final int idealSlotModMask = cached[slot]; //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; cached[gapSlot] = idealSlotModMask; gapSlot = slot; distance = 0; } } //end while // Mark the last found gap slot without a conflict as empty. keys[gapSlot] = (null); /* */ this.assigned--; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public int removeAll(final ObjectContainer other) { final int before = this.size(); //1) other is a ObjectLookupContainer, 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 ObjectLookupContainer) { if (this.allocatedDefaultKey) { if (other.contains((null))) { this.allocatedDefaultKey = false; } } final KType[] keys = ((KType[])(this.keys)); for (int i = 0; i < keys.length;) { KType existing; if (!((existing = keys[i]) == null) && 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 ObjectCursor c : other) { remove(((KType)(c.value))); } } return before - this.size(); } /** * {@inheritDoc} */ @Override public int removeAll(final ObjectPredicate predicate) { final int before = this.size(); if (this.allocatedDefaultKey) { if (predicate.apply((null))) { this.allocatedDefaultKey = false; } } final KType[] keys = ((KType[])(this.keys)); for (int i = 0; i < keys.length;) { KType existing; if (!((existing = keys[i]) == null) && predicate.apply(existing)) { shiftConflictingKeys(i); // Shift, do not increment slot. } else { i++; } } return before - this.size(); } /** * {@inheritDoc} */ @Override public int removeAll(final ObjectDoublePredicate predicate) { final int before = this.size(); if (this.allocatedDefaultKey) { if (predicate.apply((null), this.allocatedDefaultKeyValue)) { this.allocatedDefaultKey = false; } } final KType[] keys = ((KType[])(this.keys)); final double[] values = ((this.values)); for (int i = 0; i < keys.length;) { KType existing; if (!((existing = keys[i]) == null) && predicate.apply(existing, values[i])) { shiftConflictingKeys(i); // Shift, do not increment slot. } else { i++; } } return before - this.size(); } /** * {@inheritDoc} */ @Override public double get(final KType key) { if (((key) == null)) { if (this.allocatedDefaultKey) { return this.allocatedDefaultKeyValue; } return this.defaultValue; } final int mask = this.keys.length - 1; final KType[] keys = ((KType[])(this.keys)); int slot = (BitMixer.mix(hashKey((key)) , this.perturbation)) & mask; KType existing; int dist = 0; final int[] cached = this.hash_cache; while (!((existing = keys[slot]) == null) && dist <= probe_distance(slot, cached) ) { if ((equalKeys((key), (existing)))) { return ((this.values[slot])); } slot = (slot + 1) & mask; dist++; } //end while true return this.defaultValue; } /** * {@inheritDoc} */ @Override public boolean containsKey(final KType key) { if (((key) == null)) { return this.allocatedDefaultKey; } final int mask = this.keys.length - 1; final KType[] keys = ((KType[])(this.keys)); int slot = (BitMixer.mix(hashKey((key)) , this.perturbation)) & mask; KType existing; int dist = 0; final int[] cached = this.hash_cache; while (!((existing = keys[slot]) == null) && dist <= probe_distance(slot, cached) ) { if ((equalKeys((key), (existing)))) { return true; } slot = (slot + 1) & mask; dist++; } //end while true return false; } /** * {@inheritDoc} */ @Override public void clear() { this.assigned = 0; // States are always cleared. this.allocatedDefaultKey = false; //Faster than Arrays.fill(keys, null); // Help the GC. ObjectArrays.blankArray(this.keys, 0, this.keys.length); } /** * {@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() { int h = 0; if (this.allocatedDefaultKey) { h += BitMixer.mix(this.allocatedDefaultKeyValue); } final KType[] keys = ((KType[])(this.keys)); final double[] values = ((this.values)); for (int i = keys.length; --i >= 0;) { KType existing; if (!((existing = keys[i]) == null)) { h += BitMixer.mix(existing) ^ BitMixer.mix(values[i]); } } return h; } /** * {@inheritDoc} */ @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; } /* */ @SuppressWarnings("unchecked") final/* */ ObjectDoubleHashMap other = (ObjectDoubleHashMap) obj; //must be of the same size if (other.size() != this.size()) { return false; } final EntryIterator it = this.iterator(); while (it.hasNext()) { final ObjectDoubleCursor c = it.next(); if (!other.containsKey(c.key)) { //recycle it.release(); return false; } final double otherValue = other.get(c.key); if (!(Double.doubleToLongBits((c.value)) == Double.doubleToLongBits((otherValue)))) { //recycle it.release(); return false; } } //end while return true; } return false; } /** * An iterator implementation for {@link #iterator}. * Holds a ObjectDoubleCursor returning * (key, value, index) = (Object key, double value, index the position in keys {@link ObjectDoubleHashMap#keys}, or keys.length for key = 0/null) */ public final class EntryIterator extends AbstractIterator> { public final ObjectDoubleCursor cursor; public EntryIterator() { this.cursor = new ObjectDoubleCursor(); 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 ObjectDoubleCursor fetch() { if (this.cursor.index == ObjectDoubleHashMap.this.keys.length + 1) { if (ObjectDoubleHashMap.this.allocatedDefaultKey) { this.cursor.index = ObjectDoubleHashMap.this.keys.length; this.cursor.key = (null); this.cursor.value = ObjectDoubleHashMap.this.allocatedDefaultKeyValue; return this.cursor; } //no value associated with the default key, continue iteration... this.cursor.index = ObjectDoubleHashMap.this.keys.length; } int i = this.cursor.index - 1; while (i >= 0 && !(!(((((KType[])(ObjectDoubleHashMap.this.keys)))[(i)]) == null))) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.key = ((KType)(ObjectDoubleHashMap.this.keys[i])); this.cursor.value = ((ObjectDoubleHashMap.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 = ObjectDoubleHashMap.this.keys.length + 1; } @Override public void reset(final EntryIterator obj) { obj.cursor.key = null; } }); /** * {@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((null), this.allocatedDefaultKeyValue); } final KType[] keys = ((KType[])(this.keys)); final double[] values = ((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 (!((existing = keys[i]) == null)) { procedure.apply(existing, values[i]); } } return procedure; } /** * {@inheritDoc} */ @Override public > T forEach(final T predicate) { if (this.allocatedDefaultKey) { if (!predicate.apply((null), this.allocatedDefaultKeyValue)) { return predicate; } } final KType[] keys = ((KType[])(this.keys)); final double[] values = ((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 (!((existing = keys[i]) == null)) { 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 AbstractObjectCollection implements ObjectLookupContainer { private final ObjectDoubleHashMap owner = ObjectDoubleHashMap.this; @Override public boolean contains(final KType e) { return containsKey(e); } @Override public > T forEach(final T procedure) { if (this.owner.allocatedDefaultKey) { procedure.apply((null)); } final KType[] keys = ((KType[])(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 (!((existing = keys[i]) == null)) { procedure.apply(existing); } } return procedure; } @Override public > T forEach(final T predicate) { if (this.owner.allocatedDefaultKey) { if (!predicate.apply((null))) { return predicate; } } final KType[] keys = ((KType[])(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 (!((existing = keys[i]) == null)) { 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 ObjectPredicate 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 = ObjectDoubleHashMap.this.keys.length + 1; } @Override public void reset(final KeysIterator obj) { obj.cursor.value = null; } }); @Override public KType[] toArray(final KType[] target) { int count = 0; if (this.owner.allocatedDefaultKey) { target[count++] = (null); } final KType[] keys = ((KType[])(this.owner.keys)); for (int i = 0; i < keys.length; i++) { KType existing; if (!((existing = keys[i]) == null)) { target[count++] = existing; } } assert count == this.owner.size(); return target; } }; /** * An iterator over the set of keys. * Holds a ObjectCursor returning (value, index) = (Object key, index the position in buffer {@link ObjectDoubleHashMap#keys}, or keys.length for key = 0/null.) */ public final class KeysIterator extends AbstractIterator> { public final ObjectCursor cursor; public KeysIterator() { this.cursor = new ObjectCursor(); 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 ObjectCursor fetch() { if (this.cursor.index == ObjectDoubleHashMap.this.keys.length + 1) { if (ObjectDoubleHashMap.this.allocatedDefaultKey) { this.cursor.index = ObjectDoubleHashMap.this.keys.length; this.cursor.value = (null); return this.cursor; } //no value associated with the default key, continue iteration... this.cursor.index = ObjectDoubleHashMap.this.keys.length; } int i = this.cursor.index - 1; while (i >= 0 && !(!(((((KType[])(ObjectDoubleHashMap.this.keys)))[(i)]) == null))) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.value = ((KType)(ObjectDoubleHashMap.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 AbstractDoubleCollection { private final ObjectDoubleHashMap owner = ObjectDoubleHashMap.this; /** * {@inheritDoc} */ @Override public int size() { return this.owner.size(); } /** * {@inheritDoc} */ @Override public int capacity() { return this.owner.capacity(); } @Override public boolean contains(final double value) { if (this.owner.allocatedDefaultKey && (Double.doubleToLongBits((value)) == Double.doubleToLongBits((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 = ((KType[])(this.owner.keys)); final double[] values = ((this.owner.values)); for (int slot = 0; slot < keys.length; slot++) { if ((!(((keys)[(slot)]) == null)) && (Double.doubleToLongBits((value)) == Double.doubleToLongBits((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 = ((KType[])(this.owner.keys)); final double[] values = ((this.owner.values)); for (int slot = 0; slot < keys.length; slot++) { if ((!(((keys)[(slot)]) == null))) { 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 = ((KType[])(this.owner.keys)); final double[] values = ((this.owner.values)); for (int slot = 0; slot < keys.length; slot++) { if ((!(((keys)[(slot)]) == null))) { 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 double e) { final int before = this.owner.size(); if (this.owner.allocatedDefaultKey) { if ((Double.doubleToLongBits((e)) == Double.doubleToLongBits((this.owner.allocatedDefaultKeyValue)))) { this.owner.allocatedDefaultKey = false; } } final KType[] keys = ((KType[])(this.owner.keys)); final double[] values = ((this.owner.values)); for (int slot = 0; slot < keys.length;) { if ((!(((keys)[(slot)]) == null)) && (Double.doubleToLongBits((e)) == Double.doubleToLongBits((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 DoublePredicate predicate) { final int before = this.owner.size(); if (this.owner.allocatedDefaultKey) { if (predicate.apply(this.owner.allocatedDefaultKeyValue)) { this.owner.allocatedDefaultKey = false; } } final KType[] keys = ((KType[])(this.owner.keys)); final double[] values = ((this.owner.values)); for (int slot = 0; slot < keys.length;) { if ((!(((keys)[(slot)]) == null)) && 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 valuesIteratorPool = new IteratorPool( new ObjectFactory() { @Override public ValuesIterator create() { return new ValuesIterator(); } @Override public void initialize(final ValuesIterator obj) { obj.cursor.index = ObjectDoubleHashMap.this.keys.length + 1; } @Override public void reset(final ValuesIterator obj) { } }); @Override public double[] toArray(final double[] target) { int count = 0; if (this.owner.allocatedDefaultKey) { target[count++] = this.owner.allocatedDefaultKeyValue; } final KType[] keys = ((KType[])(this.owner.keys)); final double[] values = ((this.owner.values)); for (int i = 0; i < values.length; i++) { if ((!(((keys)[(i)]) == null))) { target[count++] = values[i]; } } assert count == this.owner.size(); return target; } } /** * An iterator over the set of values. * Holds a ObjectCursor returning (value, index) = (double value, index the position in buffer {@link ObjectDoubleHashMap#values}, * or values.length for value = {@link ObjectDoubleHashMap#allocatedDefaultKeyValue}). */ public final class ValuesIterator extends AbstractIterator { public final DoubleCursor cursor; public ValuesIterator() { this.cursor = new DoubleCursor(); 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 DoubleCursor fetch() { if (this.cursor.index == ObjectDoubleHashMap.this.values.length + 1) { if (ObjectDoubleHashMap.this.allocatedDefaultKey) { this.cursor.index = ObjectDoubleHashMap.this.values.length; this.cursor.value = ObjectDoubleHashMap.this.allocatedDefaultKeyValue; return this.cursor; } //no value associated with the default key, continue iteration... this.cursor.index = ObjectDoubleHashMap.this.keys.length; } int i = this.cursor.index - 1; while (i >= 0 && !(!(((((KType[])(ObjectDoubleHashMap.this.keys)))[(i)]) == null))) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.value = ((ObjectDoubleHashMap.this.values[i])); return this.cursor; } } /** * {@inheritDoc} */ @Override public ObjectDoubleHashMap clone() { //clone to size() to prevent some cases of exponential sizes, final ObjectDoubleHashMap cloned = new ObjectDoubleHashMap(this.size(), this.loadFactor); //We must NOT clone because of independent perturbations seeds cloned.putAll(this); 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 ObjectDoubleCursor 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 ObjectDoubleHashMap from(final KType[] keys, final double[] values) { if (keys.length != values.length) { throw new IllegalArgumentException("Arrays of keys and values must have an identical length."); } final ObjectDoubleHashMap map = new ObjectDoubleHashMap(keys.length); for (int i = 0; i < keys.length; i++) { map.put(keys[i], values[i]); } return map; } /** * Create a hash map from another associative container. (constructor shortcut) Default load factor is used. */ public static ObjectDoubleHashMap from( final ObjectDoubleAssociativeContainer container) { return new ObjectDoubleHashMap(container); } /** * Create a new hash map without providing the full generic signature * (constructor shortcut). */ public static ObjectDoubleHashMap newInstance() { return new ObjectDoubleHashMap(); } /** * Create a new hash map with initial capacity and load factor control. * (constructor shortcut). */ public static ObjectDoubleHashMap newInstance(final int initialCapacity, final double loadFactor) { return new ObjectDoubleHashMap(initialCapacity, loadFactor); } /** * Returns the "default value" value used in containers methods returning * "default value" */ @Override public double getDefaultValue() { return this.defaultValue; } /** * Set the "default value" value to be used in containers methods returning * "default value" */ @Override public void setDefaultValue(final double defaultValue) { this.defaultValue = defaultValue; } //Test for existence in template private int probe_distance(final int slot, final int[] cache) { final int rh = cache[slot]; if (slot < rh) { //wrap around return slot - rh + cache.length; } return slot - rh; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy