
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 extends CharBooleanCursor> 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;
}
}