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

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

package com.carrotsearch.hppcrt.maps;

import java.util.*;

import com.carrotsearch.hppcrt.*;
import com.carrotsearch.hppcrt.cursors.*;
import com.carrotsearch.hppcrt.predicates.*;
import com.carrotsearch.hppcrt.procedures.*;
import com.carrotsearch.hppcrt.strategies.*;

  
  
  
  
// If RH is defined, RobinHood Hashing is in effect :
  
/**
 * A hash map of double to byte, implemented using open
 * addressing with linear probing for collision resolution.
 *
 * The difference with {@link DoubleByteOpenHashMap} is that it uses a
 * {@link DoubleHashingStrategy} to compare objects externally instead of using
 * the built-in hashCode() /  equals(). In particular, the management of null
 * keys is up to the {@link DoubleHashingStrategy} implementation.
 * The internal buffers of this implementation ({@link #keys}), {@link #allocated})
 * 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 DoubleHashingStrategy} to * assure good performance.

* * * @author This code is inspired by the collaboration and implementation in the fastutil project. * *

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 = "2014-10-25T20:54:06+0200", value = "HPPC-RT generated from: DoubleByteOpenCustomHashMap.java") public class DoubleByteOpenCustomHashMap implements DoubleByteMap, Cloneable { /** * Minimum capacity for the map. */ public final static int MIN_CAPACITY = HashContainerUtils.MIN_CAPACITY; /** * Default capacity. */ public final static int DEFAULT_CAPACITY = HashContainerUtils.DEFAULT_CAPACITY; /** * Default load factor. */ public final static float DEFAULT_LOAD_FACTOR = HashContainerUtils.DEFAULT_LOAD_FACTOR; protected byte defaultValue = ((byte)0); /** * Hash-indexed array holding all keys. *

* Direct map iteration: iterate {keys[i], values[i]} for i in [0; keys.length[ where this.allocated[i] is true. *

*

Direct iteration warning: * If the iteration goal is to fill another hash container, please iterate {@link #keys} in reverse to prevent performance losses. * @see #values * @see #allocated */ public double[] keys; /** * Hash-indexed array holding all values associated to the keys. * stored in {@link #keys}. * * @see #keys */ public byte[] values; /** * Information if an entry (slot) in the {@link #keys} table is allocated * or empty. * * In addition it caches hash value : If = -1, it means not allocated, else = HASH(keys[i]) & mask * for every index i. * * @see #assigned */ public int[] allocated; /** * Cached number of assigned slots in {@link #allocated}. */ protected int assigned; /** * The load factor for this map (fraction of allocated slots * before the buffers must be rehashed or reallocated). */ protected final float loadFactor; /** * Resize buffers when {@link #allocated} hits this value. */ protected int resizeAt; /** * The most recent slot accessed in {@link #containsKey} (required for * {@link #lget}). * * @see #containsKey * @see #lget */ protected int lastSlot; /** * 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 DoubleHashingStrategy hashStrategy; /** * Creates a hash map with the default capacity of {@value #DEFAULT_CAPACITY}, * load factor of {@value #DEFAULT_LOAD_FACTOR}, using the hashStrategy as {@link DoubleHashingStrategy} * *

See class notes about hash distribution importance.

*/ public DoubleByteOpenCustomHashMap(final DoubleHashingStrategy hashStrategy) { this(DoubleByteOpenCustomHashMap.DEFAULT_CAPACITY, hashStrategy); } /** * Creates a hash map with the given initial capacity, default load factor of * {@value #DEFAULT_LOAD_FACTOR}, using the hashStrategy as {@link DoubleHashingStrategy} * * @param initialCapacity Initial capacity (greater than zero and automatically * rounded to the next power of two). */ public DoubleByteOpenCustomHashMap(final int initialCapacity, final DoubleHashingStrategy hashStrategy) { this(initialCapacity, DoubleByteOpenCustomHashMap.DEFAULT_LOAD_FACTOR, hashStrategy); } /** * Creates a hash map with the given initial capacity, * load factor, using the hashStrategy as {@link DoubleHashingStrategy} * * @param initialCapacity Initial capacity (greater than zero and automatically * rounded to the next power of two). * * @param loadFactor The load factor (greater than zero and smaller than 1). * * */ public DoubleByteOpenCustomHashMap(final int initialCapacity, final float loadFactor, final DoubleHashingStrategy hashStrategy) { //only accept not-null strategies. if (hashStrategy != null) { this.hashStrategy = hashStrategy; } else { throw new IllegalArgumentException("DoubleByteOpenCustomHashMap() cannot have a null hashStrategy !"); } assert loadFactor > 0 && loadFactor <= 1 : "Load factor must be between (0, 1]."; this.loadFactor = loadFactor; //take into account of the load factor to garantee no reallocations before reaching initialCapacity. int internalCapacity = (int) (initialCapacity / loadFactor) + DoubleByteOpenCustomHashMap.MIN_CAPACITY; //align on next power of two internalCapacity = HashContainerUtils.roundCapacity(internalCapacity); this.keys = new double [internalCapacity]; this.values = new byte [internalCapacity]; //fill with "not allocated" value this.allocated = new int[internalCapacity]; Internals.blankIntArrayMinusOne(this.allocated, 0, this.allocated.length); //Take advantage of the rounding so that the resize occur a bit later than expected. //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 = Math.max(3, (int) (internalCapacity * loadFactor)) - 2; } /** * Create a hash map from all key-value pairs of another container. */ public DoubleByteOpenCustomHashMap(final DoubleByteAssociativeContainer container, final DoubleHashingStrategy hashStrategy) { this(container.size(), hashStrategy); putAll(container); } /** * {@inheritDoc} */ @Override public byte put(double key, byte value) { assert this.assigned < this.allocated.length; final int mask = this.allocated.length - 1; final DoubleHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; final double[] keys = this.keys; final byte[] values = this.values; final int[] allocated = this.allocated; double tmpKey; byte tmpValue; int tmpAllocated; int initial_slot = slot; int dist = 0; int existing_distance = 0; while (allocated[slot] != -1 ) { if (strategy.equals(key, keys[slot])) { final byte oldValue = values[slot]; values[slot] = value; return oldValue; } //re-shuffle keys to minimize variance existing_distance = (slot < allocated[slot] ? slot + allocated.length - allocated[slot] : slot - allocated[slot]); if (dist > existing_distance) { //swap current (key, value, initial_slot) with slot places tmpKey = keys[slot]; keys[slot] = key; key = tmpKey; tmpAllocated = allocated[slot]; allocated[slot] = initial_slot; initial_slot = tmpAllocated; tmpValue = values[slot]; values[slot] = value; value = tmpValue; dist = existing_distance; } slot = (slot + 1) & mask; dist++; } // 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++; allocated[slot] = initial_slot; keys[slot] = key; values[slot] = value; } return this.defaultValue; } /** * {@inheritDoc} */ @Override public int putAll(final DoubleByteAssociativeContainer container) { return putAll((Iterable) container); } /** * {@inheritDoc} */ @Override public int putAll(final Iterable iterable) { final int count = this.assigned; for (final DoubleByteCursor c : iterable) { put(c.key, c.value); } return this.assigned - count; } /** * {@inheritDoc} */ @Override public boolean putIfAbsent(final double key, final byte value) { if (!containsKey(key)) { put(key, value); return true; } return false; } /** * Trove-inspired API method. An equivalent * of the following code: *
     *  if (containsKey(key))
     *  {
     *      byte v = (byte) (lget() + additionValue);
     *      lset(v);
     *      return v;
     *  }
     *  else
     *  {
     *     put(key, putValue);
     *     return putValue;
     *  }
     * 
* * @param key The key of the value to adjust. * @param putValue The value to put if key does not exist. * @param additionValue The value to add to the existing value if key exists. * @return Returns the current value associated with key (after changes). */ @Override public byte putOrAdd(double key, byte putValue, byte additionValue) { assert assigned < allocated.length; final int mask = allocated.length - 1; final DoubleHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; final int[] allocated = this.allocated; final double[] keys = this.keys; final byte[] values = this.values; byte value = this.defaultValue; double tmpKey; byte tmpValue; int tmpAllocated; int initial_slot = slot; int dist = 0; int existing_distance = 0; while (allocated[slot] != -1 ) { existing_distance = (slot < allocated[slot] ? slot + allocated.length - allocated[slot] : slot - allocated[slot]); if (strategy.equals(key, keys[slot])) { values[slot] += additionValue; return values[slot]; } else if (dist > existing_distance) { //swap current (key, value, initial_slot) with slot places tmpKey = keys[slot]; keys[slot] = key; key = tmpKey; tmpValue = values[slot]; values[slot] = value; value = tmpValue; tmpAllocated = allocated[slot]; allocated[slot] = initial_slot; initial_slot = tmpAllocated; dist = existing_distance; } slot = (slot + 1) & mask; dist++; } if (assigned == resizeAt) { expandAndPut(key, putValue, slot); } else { assigned++; allocated[slot] = initial_slot; keys[slot] = key; values[slot] = putValue; } return putValue; } /** * An equivalent of calling *
     *  if (containsKey(key))
     *  {
     *      byte v = (byte) (lget() + additionValue);
     *      lset(v);
     *      return v;
     *  }
     *  else
     *  {
     *     put(key, additionValue);
     *     return additionValue;
     *  }
     * 
* * @param key The key of the value to adjust. * @param additionValue 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 byte addTo(double key, byte additionValue) { return putOrAdd(key, additionValue, additionValue); } /** * Expand the internal storage buffers (capacity) and rehash. */ private void expandAndPut(final double pendingKey, final byte pendingValue, final int freeSlot) { assert this.assigned == this.resizeAt; assert this.allocated[freeSlot] == -1; // Try to allocate new buffers first. If we OOM, it'll be now without // leaving the data structure in an inconsistent state. final double[] oldKeys = this.keys; final byte[] oldValues = this.values; final int[] oldAllocated = this.allocated; allocateBuffers(HashContainerUtils.nextCapacity(this.keys.length)); // We have succeeded at allocating new data so insert the pending key/value at // the free slot in the old arrays before rehashing. this.lastSlot = -1; this.assigned++; //We don't care of the oldAllocated value, so long it means "allocated = true", since the whole set is rebuilt from scratch. oldAllocated[freeSlot] = 1; oldKeys[freeSlot] = pendingKey; oldValues[freeSlot] = pendingValue; //for inserts final int mask = this.allocated.length - 1; final DoubleHashingStrategy strategy = this.hashStrategy; double key = (0.0D); byte value = ((byte)0); int slot = -1; final double[] keys = this.keys; final byte[] values = this.values; final int[] allocated = this.allocated; double tmpKey = (0.0D); byte tmpValue = ((byte)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 ! for (int i = oldAllocated.length; --i >= 0;) { if (oldAllocated[i] != -1 ) { key = oldKeys[i]; value = oldValues[i]; slot = Internals.rehash(strategy.computeHashCode(key)) & mask; initial_slot = slot; dist = 0; while (allocated[slot] != -1 ) { //re-shuffle keys to minimize variance existing_distance = (slot < allocated[slot] ? slot + allocated.length - allocated[slot] : slot - allocated[slot]); if (dist > existing_distance) { //swap current (key, value, initial_slot) with slot places tmpKey = keys[slot]; keys[slot] = key; key = tmpKey; tmpAllocated = allocated[slot]; allocated[slot] = initial_slot; initial_slot = tmpAllocated; tmpValue = values[slot]; values[slot] = value; value = tmpValue; dist = existing_distance; } slot = (slot + 1) & mask; dist++; } //end while allocated[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). */ private void allocateBuffers(final int capacity) { final double[] keys = new double [capacity]; final byte[] values = new byte [capacity]; final int[] allocated = new int[capacity]; Internals.blankIntArrayMinusOne(allocated, 0, allocated.length); this.keys = keys; this.values = values; this.allocated = allocated; //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 = Math.max(3, (int) (capacity * this.loadFactor)) - 2; } /** * {@inheritDoc} */ @Override public byte remove(final double key) { final int mask = this.allocated.length - 1; final DoubleHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; int dist = 0; final double[] keys = this.keys; final int[] states = this.allocated; while (states[slot] != -1 && dist <= (slot < states[slot] ? slot + states.length - states[slot] : slot - states[slot]) ) { if (strategy.equals(key, keys[slot])) { final byte value = this.values[slot]; this.assigned--; 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. */ protected void shiftConflictingKeys(int slotCurr) { // Copied nearly verbatim from fastutil's impl. final int mask = this.allocated.length - 1; int slotPrev, slotOther; final DoubleHashingStrategy strategy = this.hashStrategy; final double[] keys = this.keys; final byte[] values = this.values; final int[] allocated = this.allocated; while (true) { slotCurr = ((slotPrev = slotCurr) + 1) & mask; while (allocated[slotCurr] != -1 ) { //use the cached value, no need to recompute slotOther = allocated[slotCurr]; if (slotPrev <= slotCurr) { // we're on the right of the original slot. if (slotPrev >= slotOther || slotOther > slotCurr) { break; } } else { // we've wrapped around. if (slotPrev >= slotOther && slotOther > slotCurr) { break; } } slotCurr = (slotCurr + 1) & mask; } if ( allocated[slotCurr] == -1 ) { break; } // Shift key/value/allocated triplet. keys[slotPrev] = keys[slotCurr]; values[slotPrev] = values[slotCurr]; allocated[slotPrev] = allocated[slotCurr]; } //means not allocated allocated[slotPrev] = -1; /* */ /* */ } /** * {@inheritDoc} */ @Override public int removeAll(final DoubleContainer container) { final int before = this.assigned; for (final DoubleCursor cursor : container) { remove(cursor.value); } return before - this.assigned; } /** * {@inheritDoc} *

Important! * If the predicate actually injects the removed keys in another hash container, you may experience performance losses. */ @Override public int removeAll(final DoublePredicate predicate) { final int before = this.assigned; final double[] keys = this.keys; final int[] states = this.allocated; for (int i = 0; i < states.length;) { if (states[i] != -1 ) { if (predicate.apply(keys[i])) { this.assigned--; shiftConflictingKeys(i); // Repeat the check for the same i. continue; } } i++; } return before - this.assigned; } /** * {@inheritDoc} * *

Use the following snippet of code to check for key existence * first and then retrieve the value if it exists.

*
     * if (map.containsKey(key))
     *   value = map.lget();
     * 
*/ @Override public byte get(final double key) { final int mask = this.allocated.length - 1; final DoubleHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; int dist = 0; final double[] keys = this.keys; final int[] states = this.allocated; while (states[slot] != -1 && dist <= (slot < states[slot] ? slot + states.length - states[slot] : slot - states[slot]) ) { if (strategy.equals(key, keys[slot])) { return this.values[slot]; } slot = (slot + 1) & mask; dist++; } //end while true return this.defaultValue; } /** * Returns the last key stored in this has map for the corresponding * most recent call to {@link #containsKey}. * Precondition : {@link #containsKey} must have been called previously ! *

Use the following snippet of code to check for key existence * first and then retrieve the key value if it exists.

*
     * if (map.containsKey(key))
     *   value = map.lkey();
     * 
* *

This is equivalent to calling:

*
     * if (map.containsKey(key))
     *   key = map.keys[map.lslot()];
     * 
*/ public double lkey() { assert this.lastSlot >= 0 : "Call containsKey() first."; assert this.allocated[this.lastSlot] != -1 : "Last call to exists did not have any associated value."; return this.keys[this.lastSlot]; } /** * Returns the last value saved in a call to {@link #containsKey}. * Precondition : {@link #containsKey} must have been called previously ! * @see #containsKey */ public byte lget() { assert this.lastSlot >= 0 : "Call containsKey() first."; assert this.allocated[this.lastSlot] != -1 : "Last call to exists did not have any associated value."; return this.values[this.lastSlot]; } /** * Sets the value corresponding to the key saved in the last * call to {@link #containsKey}, if and only if the key exists * in the map already. * Precondition : {@link #containsKey} must have been called previously ! * @see #containsKey * @return Returns the previous value stored under the given key. */ public byte lset(final byte key) { assert this.lastSlot >= 0 : "Call containsKey() first."; assert this.allocated[this.lastSlot] != -1 : "Last call to exists did not have any associated value."; final byte previous = this.values[this.lastSlot]; this.values[this.lastSlot] = key; return previous; } /** * @return Returns the slot of the last key looked up in a call to {@link #containsKey} if * it returned true. * * @see #containsKey */ public int lslot() { assert this.lastSlot >= 0 : "Call containsKey() first."; return this.lastSlot; } /** * {@inheritDoc} * *

Saves the associated value for fast access using {@link #lget} * or {@link #lset}.

*
     * if (map.containsKey(key))
     *   value = map.lget();
     * 
* or, to modify the value at the given key without looking up * its slot twice: *
     * if (map.containsKey(key))
     *   map.lset(map.lget() + 1);
     * 
* */ @Override public boolean containsKey(final double key) { final int mask = this.allocated.length - 1; final DoubleHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; int dist = 0; final double[] keys = this.keys; final int[] states = this.allocated; while (states[slot] != -1 && dist <= (slot < states[slot] ? slot + states.length - states[slot] : slot - states[slot]) ) { if (strategy.equals(key, keys[slot])) { this.lastSlot = slot; return true; } slot = (slot + 1) & mask; dist++; } //end while true //unsuccessful search this.lastSlot = -1; return false; } /** * {@inheritDoc} * *

Does not release internal buffers.

*/ @Override public void clear() { this.assigned = 0; this.lastSlot = -1; // States are always cleared. Internals.blankIntArrayMinusOne(this.allocated, 0, this.allocated.length); } /** * {@inheritDoc} */ @Override public int size() { return this.assigned; } /** * {@inheritDoc} */ @Override public int capacity() { return this.resizeAt - 1; } /** * {@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; final double[] keys = this.keys; final byte[] values = this.values; final int[] states = this.allocated; final DoubleHashingStrategy strategy = this.hashStrategy; for (int i = states.length; --i >= 0;) { if (states[i] != -1 ) { //This hash is an intrinsic property of the container contents, //consequently is independent from the HashStrategy, so do not use it ! h += Internals.rehash(strategy.computeHashCode(keys[i])) + Internals.rehash(values[i]); } } return h; } /** * this instance and obj can only be equal if :
     * (both are DoubleByteOpenCustomHashMap)
     * and
     * (both have equal hash strategies defined by {@link #DoubleHashingStrategy}.equals(obj.hashStrategy))
* then, both maps are compared using their {@link #DoubleHashingStrategy}. */ @Override public boolean equals(final Object obj) { if (obj != null) { if (obj == this) { return true; } if (!(obj instanceof DoubleByteOpenCustomHashMap)) { return false; } if (!this.hashStrategy.equals(((DoubleByteOpenCustomHashMap) obj).hashStrategy)) { return false; } @SuppressWarnings("unchecked") final DoubleByteOpenCustomHashMap other = (DoubleByteOpenCustomHashMap) obj; if (other.size() == this.size()) { final EntryIterator it = this.iterator(); while (it.hasNext()) { final DoubleByteCursor c = it.next(); if (other.containsKey(c.key)) { final byte v = other.get(c.key); if (((c.value) == (v))) { continue; } } //recycle it.release(); return false; } return true; } } return false; } /** * An iterator implementation for {@link #iterator}. */ public final class EntryIterator extends AbstractIterator { public final DoubleByteCursor cursor; public EntryIterator() { this.cursor = new DoubleByteCursor(); 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 DoubleByteCursor fetch() { int i = this.cursor.index - 1; while (i >= 0 && DoubleByteOpenCustomHashMap.this.allocated[i] == -1 ) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.key = DoubleByteOpenCustomHashMap.this.keys[i]; this.cursor.value = DoubleByteOpenCustomHashMap.this.values[i]; return this.cursor; } } /** * internal pool of EntryIterator */ protected final IteratorPool entryIteratorPool = new IteratorPool( new ObjectFactory() { @Override public EntryIterator create() { return new EntryIterator(); } @Override public void initialize(final EntryIterator obj) { obj.cursor.index = DoubleByteOpenCustomHashMap.this.keys.length; } @Override public void reset(final EntryIterator obj) { // nothing } }); /** * {@inheritDoc} * @return */ @Override public EntryIterator iterator() { //return new EntryIterator(); return this.entryIteratorPool.borrow(); } /** * {@inheritDoc} */ @Override public T forEach(final T procedure) { final double[] keys = this.keys; final byte[] values = this.values; final int[] states = this.allocated; //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 = states.length - 1; i >= 0; i--) { if (states[i] != -1 ) { procedure.apply(keys[i], values[i]); } } return procedure; } /** * {@inheritDoc} */ @Override public T forEach(final T predicate) { final double[] keys = this.keys; final byte[] values = this.values; final int[] states = this.allocated; //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 = states.length - 1; i >= 0; i--) { if (states[i] != -1 ) { if (!predicate.apply(keys[i], values[i])) { break; } } } //end for return predicate; } /** * @return a new KeysContainer view of the keys of this associated container. * This view then reflects all changes from the map. */ @Override public KeysContainer keys() { return new KeysContainer(); } /** * A view of the keys inside this hash map. */ public final class KeysContainer extends AbstractDoubleCollection implements DoubleLookupContainer { private final DoubleByteOpenCustomHashMap owner = DoubleByteOpenCustomHashMap.this; @Override public boolean contains(final double e) { return containsKey(e); } @Override public T forEach(final T procedure) { final double[] keys = this.owner.keys; final int[] states = this.owner.allocated; //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 = states.length - 1; i >= 0; i--) { if (states[i] != -1 ) { procedure.apply(keys[i]); } } return procedure; } @Override public T forEach(final T predicate) { final double[] keys = this.owner.keys; final int[] states = this.owner.allocated; //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 = states.length - 1; i >= 0; i--) { if (states[i] != -1 ) { if (!predicate.apply(keys[i])) { 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 DoublePredicate predicate) { return this.owner.removeAll(predicate); } @Override public int removeAllOccurrences(final double 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 keyIteratorPool = new IteratorPool( new ObjectFactory() { @Override public KeysIterator create() { return new KeysIterator(); } @Override public void initialize(final KeysIterator obj) { obj.cursor.index = DoubleByteOpenCustomHashMap.this.keys.length; } @Override public void reset(final KeysIterator obj) { // nothing } }); @Override public double[] toArray(final double[] target) { final double[] keys = this.owner.keys; final int[] states = this.owner.allocated; int count = 0; for (int i = 0; i < keys.length; i++) { if (states[i] != -1 ) { target[count++] = keys[i]; } } assert count == this.owner.assigned; return target; } }; /** * An iterator over the set of assigned keys. */ public final class KeysIterator extends AbstractIterator { public final DoubleCursor cursor; public KeysIterator() { 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() { int i = this.cursor.index - 1; while (i >= 0 && DoubleByteOpenCustomHashMap.this.allocated[i] == -1 ) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.value = DoubleByteOpenCustomHashMap.this.keys[i]; return this.cursor; } } /** * @return a new ValuesContainer, view of the values of this map. * This view then reflects all changes from the map. */ @Override public ValuesContainer values() { return new ValuesContainer(); } /** * A view over the set of values of this map. */ public final class ValuesContainer extends AbstractByteCollection { private final DoubleByteOpenCustomHashMap owner = DoubleByteOpenCustomHashMap.this; /** * {@inheritDoc} */ @Override public int size() { return this.owner.size(); } /** * {@inheritDoc} */ @Override public int capacity() { return this.owner.capacity(); } @Override public boolean contains(final byte value) { // This is a linear scan over the values, but it's in the contract, so be it. final int[] states = this.owner.allocated; final byte[] values = this.owner.values; for (int slot = 0; slot < states.length; slot++) { if ( states[slot] != -1 && ((value) == (values[slot]))) { return true; } } return false; } @Override public T forEach(final T procedure) { final int[] states = this.owner.allocated; final byte[] values = this.owner.values; for (int slot = 0; slot < states.length; slot++) { if ( states[slot] != -1 ) { procedure.apply(values[slot]); } } return procedure; } @Override public T forEach(final T predicate) { final int[] states = this.owner.allocated; final byte[] values = this.owner.values; for (int slot = 0; slot < states.length; slot++) { if ( states[slot] != -1 ) { 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 removeAllOccurrences(final byte e) { final int before = this.owner.assigned; final byte[] values = this.owner.values; final int[] states = this.owner.allocated; for (int i = 0; i < states.length;) { if (states[i] != -1 ) { if (((e) == (values[i]))) { this.owner.assigned--; shiftConflictingKeys(i); // Repeat the check for the same i. continue; } } i++; } return before - this.owner.assigned; } /** * {@inheritDoc} * Indeed removes all the (key,value) pairs matching * the predicate for the values, from the map. */ @Override public int removeAll(final BytePredicate predicate) { final int before = this.owner.assigned; final byte[] values = this.owner.values; final int[] states = this.owner.allocated; for (int i = 0; i < states.length;) { if (states[i] != -1 ) { if (predicate.apply(values[i])) { this.owner.assigned--; shiftConflictingKeys(i); // Repeat the check for the same i. continue; } } i++; } return before - this.owner.assigned; } /** * {@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 = DoubleByteOpenCustomHashMap.this.keys.length; } @Override public void reset(final ValuesIterator obj) { // nothing } }); @Override public byte[] toArray(final byte[] target) { final int[] states = this.owner.allocated; final byte[] values = this.owner.values; int count = 0; for (int i = 0; i < values.length; i++) { if (states[i] != -1 ) { target[count++] = values[i]; } } assert count == this.owner.assigned; return target; } } /** * An iterator over the set of assigned values. */ public final class ValuesIterator extends AbstractIterator { public final ByteCursor cursor; public ValuesIterator() { this.cursor = new ByteCursor(); 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 ByteCursor fetch() { int i = this.cursor.index - 1; while (i >= 0 && DoubleByteOpenCustomHashMap.this.allocated[i] == -1 ) { i--; } if (i == -1) { return done(); } this.cursor.index = i; this.cursor.value = DoubleByteOpenCustomHashMap.this.values[i]; return this.cursor; } } /** * Clone this object. * */ @Override public DoubleByteOpenCustomHashMap clone() { /* */ DoubleByteOpenCustomHashMap cloned = new DoubleByteOpenCustomHashMap(this.size(), this.loadFactor, this.hashStrategy); cloned.putAll(this); 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 DoubleByteCursor 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. */ public static DoubleByteOpenCustomHashMap from(final double[] keys, final byte[] values, final DoubleHashingStrategy hashStrategy) { if (keys.length != values.length) { throw new IllegalArgumentException("Arrays of keys and values must have an identical length."); } final DoubleByteOpenCustomHashMap map = new DoubleByteOpenCustomHashMap(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. */ public static DoubleByteOpenCustomHashMap from(final DoubleByteAssociativeContainer container, final DoubleHashingStrategy hashStrategy) { return new DoubleByteOpenCustomHashMap(container, hashStrategy); } /** * Create a new hash map without providing the full generic signature (constructor * shortcut). */ public static DoubleByteOpenCustomHashMap newInstance(final DoubleHashingStrategy hashStrategy) { return new DoubleByteOpenCustomHashMap(hashStrategy); } /** * Create a new hash map with initial capacity and load factor control. (constructor * shortcut). */ public static DoubleByteOpenCustomHashMap newInstance(final int initialCapacity, final float loadFactor, final DoubleHashingStrategy hashStrategy) { return new DoubleByteOpenCustomHashMap(initialCapacity, loadFactor, hashStrategy); } /** * Return the current {@link DoubleHashingStrategy} in use. */ public DoubleHashingStrategy strategy() { return this.hashStrategy; } /** * Returns the "default value" value used * in containers methods returning "default value" * @return */ public byte getDefaultValue() { return this.defaultValue; } /** * Set the "default value" value to be used * in containers methods returning "default value" * @return */ public void setDefaultValue(final byte defaultValue) { this.defaultValue = defaultValue; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy