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

com.carrotsearch.hppcrt.maps.CharBooleanOpenCustomHashMap 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 char to boolean, implemented using open
 * addressing with linear probing for collision resolution.
 *
 * The difference with {@link CharBooleanOpenHashMap} is that it uses a
 * {@link CharHashingStrategy} to compare objects externally instead of using
 * the built-in hashCode() /  equals(). In particular, the management of null
 * keys is up to the {@link CharHashingStrategy} 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 CharHashingStrategy} 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-08-19T19:56:33+0200", value = "HPPC-RT generated from: CharBooleanOpenCustomHashMap.java") public class CharBooleanOpenCustomHashMap implements CharBooleanMap, 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 boolean defaultValue = (false); /** * 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. *

* @see #values * @see #allocated */ public char[] keys; /** * Hash-indexed array holding all values associated to the keys * stored in {@link #keys}. * * @see #keys */ public boolean[] values; /** * Information if an entry (slot) in the {@link #values} 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 CharHashingStrategy 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 CharHashingStrategy} * *

See class notes about hash distribution importance.

*/ public CharBooleanOpenCustomHashMap(final CharHashingStrategy hashStrategy) { this(CharBooleanOpenCustomHashMap.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 CharHashingStrategy} * * @param initialCapacity Initial capacity (greater than zero and automatically * rounded to the next power of two). */ public CharBooleanOpenCustomHashMap(final int initialCapacity, final CharHashingStrategy hashStrategy) { this(initialCapacity, CharBooleanOpenCustomHashMap.DEFAULT_LOAD_FACTOR, hashStrategy); } /** * Creates a hash map with the given initial capacity, * load factor, using the hashStrategy as {@link CharHashingStrategy} * * @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 CharBooleanOpenCustomHashMap(final int initialCapacity, final float loadFactor, final CharHashingStrategy hashStrategy) { //only accept not-null strategies. if (hashStrategy != null) { this.hashStrategy = hashStrategy; } else { throw new IllegalArgumentException("CharBooleanOpenCustomHashMap() 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) + CharBooleanOpenCustomHashMap.MIN_CAPACITY; //align on next power of two internalCapacity = HashContainerUtils.roundCapacity(internalCapacity); this.keys = new char [internalCapacity]; this.values = new boolean [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 CharBooleanOpenCustomHashMap(final CharBooleanAssociativeContainer container, final CharHashingStrategy hashStrategy) { this(container.size(), hashStrategy); putAll(container); } /** * {@inheritDoc} */ @Override public boolean put(char key, boolean value) { assert assigned < allocated.length; final int mask = allocated.length - 1; final CharHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; final char[] keys = this.keys; final boolean[] values = this.values; final int[] allocated = this.allocated; char tmpKey; boolean 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 boolean 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 (assigned == resizeAt) { expandAndPut(key, value, slot); } else { assigned++; allocated[slot] = initial_slot; keys[slot] = key; values[slot] = value; } return this.defaultValue; } /** * {@inheritDoc} */ @Override public int putAll(final CharBooleanAssociativeContainer container) { final int count = this.assigned; for (final CharBooleanCursor c : container) { put(c.key, c.value); } return this.assigned - count; } /** * {@inheritDoc} */ @Override public int putAll(final Iterable iterable) { final int count = this.assigned; for (final CharBooleanCursor c : iterable) { put(c.key, c.value); } return this.assigned - count; } /** * {@inheritDoc} */ @Override public boolean putIfAbsent(final char key, final boolean value) { if (!containsKey(key)) { put(key, value); return true; } return false; } /** * Expand the internal storage buffers (capacity) and rehash. */ private void expandAndPut(final char pendingKey, final boolean pendingValue, final int freeSlot) { assert assigned == resizeAt; assert 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 char[] oldKeys = this.keys; final boolean[] oldValues = this.values; final int[] oldAllocated = this.allocated; allocateBuffers(HashContainerUtils.nextCapacity(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. lastSlot = -1; 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 CharHashingStrategy strategy = this.hashStrategy; char key = ('\u0000'); boolean value = (false); int slot = -1; final char[] keys = this.keys; final boolean[] values = this.values; final int[] allocated = this.allocated; char tmpKey = ('\u0000'); boolean tmpValue = (false); 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 char[] keys = new char [capacity]; final boolean[] values = new boolean [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 * loadFactor)) - 2; } /** * {@inheritDoc} */ @Override public boolean remove(final char key) { final int mask = allocated.length - 1; final CharHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; int dist = 0; final char[] 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 boolean value = 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 = allocated.length - 1; int slotPrev, slotOther; final CharHashingStrategy strategy = this.hashStrategy; final char[] keys = this.keys; final boolean[] 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 CharContainer container) { final int before = this.assigned; for (final CharCursor cursor : container) { remove(cursor.value); } return before - this.assigned; } /** * {@inheritDoc} */ @Override public int removeAll(final CharPredicate predicate) { final int before = this.assigned; final char[] keys = this.keys; final int[] states = this.allocated; for (int i = 0; i < states.length;) { if (states[i] != -1 ) { if (predicate.apply(keys[i])) { 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 boolean get(final char key) { final int mask = allocated.length - 1; final CharHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; int dist = 0; final char[] 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 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 char lkey() { assert lastSlot >= 0 : "Call containsKey() first."; assert allocated[lastSlot] != -1 : "Last call to exists did not have any associated value."; return keys[lastSlot]; } /** * Returns the last value saved in a call to {@link #containsKey}. * Precondition : {@link #containsKey} must have been called previously ! * @see #containsKey */ public boolean lget() { assert lastSlot >= 0 : "Call containsKey() first."; assert allocated[lastSlot] != -1 : "Last call to exists did not have any associated value."; return values[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 boolean lset(final boolean key) { assert lastSlot >= 0 : "Call containsKey() first."; assert allocated[lastSlot] != -1 : "Last call to exists did not have any associated value."; final boolean previous = values[lastSlot]; values[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 lastSlot >= 0 : "Call containsKey() first."; return 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 char key) { final int mask = allocated.length - 1; final CharHashingStrategy strategy = this.hashStrategy; int slot = Internals.rehash(strategy.computeHashCode(key)) & mask; int dist = 0; final char[] 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 return false; } /** * {@inheritDoc} * *

Does not release internal buffers.

*/ @Override public void clear() { assigned = 0; lastSlot = -1; // States are always cleared. Internals.blankIntArrayMinusOne(allocated, 0, allocated.length); } /** * {@inheritDoc} */ @Override public int size() { return assigned; } /** * {@inheritDoc} */ @Override public int capacity() { return 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 char[] keys = this.keys; final boolean[] values = this.values; final int[] states = this.allocated; final CharHashingStrategy 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 CharBooleanOpenCustomHashMap)
     * and
     * (both have equal hash strategies defined by {@link #CharHashingStrategy}.equals(obj.hashStrategy))
* then, both maps are compared using their {@link #CharHashingStrategy}. */ @Override public boolean equals(final Object obj) { if (obj != null) { if (obj == this) return true; if (!(obj instanceof CharBooleanOpenCustomHashMap)) { return false; } if (!this.hashStrategy.equals(((CharBooleanOpenCustomHashMap) obj).hashStrategy)) { return false; } @SuppressWarnings("unchecked") final CharBooleanOpenCustomHashMap other = (CharBooleanOpenCustomHashMap) obj; if (other.size() == this.size()) { final EntryIterator it = this.iterator(); while (it.hasNext()) { final CharBooleanCursor c = it.next(); if (other.containsKey(c.key)) { final boolean 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 CharBooleanCursor cursor; public EntryIterator() { cursor = new CharBooleanCursor(); cursor.index = -2; } /** * Iterate backwards w.r.t the buffer, to * minimize collision chains when filling another hash container (ex. with putAll()) */ @Override protected CharBooleanCursor fetch() { int i = cursor.index - 1; while (i >= 0 && allocated[i] == -1 ) { i--; } if (i == -1) return done(); cursor.index = i; cursor.key = keys[i]; cursor.value = values[i]; return 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 = 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 char[] keys = this.keys; final boolean[] values = this.values; final int[] states = this.allocated; for (int i = 0; i < states.length; i++) { if (states[i] != -1 ) procedure.apply(keys[i], values[i]); } return procedure; } /** * {@inheritDoc} */ @Override public T forEach(final T predicate) { final char[] keys = this.keys; final boolean[] values = this.values; final int[] states = this.allocated; for (int i = 0; i < states.length; 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 AbstractCharCollection implements CharLookupContainer { private final CharBooleanOpenCustomHashMap owner = CharBooleanOpenCustomHashMap.this; @Override public boolean contains(final char e) { return containsKey(e); } @Override public T forEach(final T procedure) { final char[] keys = owner.keys; final int[] states = owner.allocated; for (int i = 0; i < states.length; i++) { if (states[i] != -1 ) procedure.apply(keys[i]); } return procedure; } @Override public T forEach(final T predicate) { final char[] keys = owner.keys; final int[] states = owner.allocated; for (int i = 0; i < states.length; 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 owner.size(); } /** * {@inheritDoc} */ @Override public int capacity() { return owner.capacity(); } @Override public void clear() { owner.clear(); } @Override public int removeAll(final CharPredicate predicate) { return owner.removeAll(predicate); } @Override public int removeAllOccurrences(final char e) { final boolean hasKey = owner.containsKey(e); int result = 0; if (hasKey) { 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 = keys.length; } @Override public void reset(final KeysIterator obj) { // nothing } }); @Override public char[] toArray(final char[] target) { final char[] keys = owner.keys; final int[] states = owner.allocated; int count = 0; for (int i = 0; i < keys.length; i++) { if (states[i] != -1 ) { target[count++] = keys[i]; } } assert count == owner.assigned; return target; } }; /** * An iterator over the set of assigned keys. */ public final class KeysIterator extends AbstractIterator { public final CharCursor cursor; public KeysIterator() { cursor = new CharCursor(); cursor.index = -2; } /** * Iterate backwards w.r.t the buffer, to * minimize collision chains when filling another hash container (ex. with putAll()) */ @Override protected CharCursor fetch() { int i = cursor.index - 1; while (i >= 0 && allocated[i] == -1 ) { i--; } if (i == -1) return done(); cursor.index = i; cursor.value = keys[i]; return 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 AbstractBooleanCollection { private final CharBooleanOpenCustomHashMap owner = CharBooleanOpenCustomHashMap.this; /** * {@inheritDoc} */ @Override public int size() { return owner.size(); } /** * {@inheritDoc} */ @Override public int capacity() { return owner.capacity(); } @Override public boolean contains(final boolean value) { // This is a linear scan over the values, but it's in the contract, so be it. final int[] states = owner.allocated; final boolean[] values = 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 = owner.allocated; final boolean[] values = 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 = owner.allocated; final boolean[] values = 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 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 boolean e) { final int before = owner.assigned; final boolean[] values = owner.values; final int[] states = owner.allocated; for (int i = 0; i < states.length;) { if (states[i] != -1 ) { if (((e) == (values[i]))) { owner.assigned--; shiftConflictingKeys(i); // Repeat the check for the same i. continue; } } i++; } return before - owner.assigned; } /** * {@inheritDoc} * Indeed removes all the (key,value) pairs matching * the predicate for the values, from the map. */ @Override public int removeAll(final BooleanPredicate predicate) { final int before = owner.assigned; final boolean[] values = owner.values; final int[] states = owner.allocated; for (int i = 0; i < states.length;) { if (states[i] != -1 ) { if (predicate.apply(values[i])) { owner.assigned--; shiftConflictingKeys(i); // Repeat the check for the same i. continue; } } i++; } return before - owner.assigned; } /** * {@inheritDoc} * Alias for clear() the whole map. */ @Override public void clear() { 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 = keys.length; } @Override public void reset(final ValuesIterator obj) { // nothing } }); @Override public boolean[] toArray(final boolean[] target) { final int[] states = owner.allocated; final boolean[] values = owner.values; int count = 0; for (int i = 0; i < values.length; i++) { if (states[i] != -1 ) { target[count++] = values[i]; } } assert count == owner.assigned; return target; } } /** * An iterator over the set of assigned values. */ public final class ValuesIterator extends AbstractIterator { public final BooleanCursor cursor; public ValuesIterator() { cursor = new BooleanCursor(); cursor.index = -2; } /** * Iterate backwards w.r.t the buffer, to * minimize collision chains when filling another hash container (ex. with putAll()) */ @Override protected BooleanCursor fetch() { int i = cursor.index - 1; while (i >= 0 && allocated[i] == -1 ) { i--; } if (i == -1) return done(); cursor.index = i; cursor.value = values[i]; return cursor; } } /** * Clone this object. * */ @Override public CharBooleanOpenCustomHashMap clone() { /* */ CharBooleanOpenCustomHashMap cloned = new CharBooleanOpenCustomHashMap(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 CharBooleanCursor 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 CharBooleanOpenCustomHashMap from(final char[] keys, final boolean[] values, final CharHashingStrategy hashStrategy) { if (keys.length != values.length) throw new IllegalArgumentException("Arrays of keys and values must have an identical length."); final CharBooleanOpenCustomHashMap map = new CharBooleanOpenCustomHashMap(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 CharBooleanOpenCustomHashMap from(final CharBooleanAssociativeContainer container, final CharHashingStrategy hashStrategy) { return new CharBooleanOpenCustomHashMap(container, hashStrategy); } /** * Create a new hash map without providing the full generic signature (constructor * shortcut). */ public static CharBooleanOpenCustomHashMap newInstance(final CharHashingStrategy hashStrategy) { return new CharBooleanOpenCustomHashMap(hashStrategy); } /** * Create a new hash map with initial capacity and load factor control. (constructor * shortcut). */ public static CharBooleanOpenCustomHashMap newInstance(final int initialCapacity, final float loadFactor, final CharHashingStrategy hashStrategy) { return new CharBooleanOpenCustomHashMap(initialCapacity, loadFactor, hashStrategy); } /** * Return the current {@link CharHashingStrategy} in use. */ public CharHashingStrategy strategy() { return this.hashStrategy; } /** * Returns the "default value" value used * in containers methods returning "default value" * @return */ public boolean getDefaultValue() { return defaultValue; } /** * Set the "default value" value to be used * in containers methods returning "default value" * @return */ public void setDefaultValue(final boolean defaultValue) { this.defaultValue = defaultValue; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy