com.carrotsearch.hppc.DoubleCharOpenHashMap Maven / Gradle / Ivy
Show all versions of hppc Show documentation
package com.carrotsearch.hppc;
import java.util.*;
import com.carrotsearch.hppc.cursors.*;
import com.carrotsearch.hppc.hash.MurmurHash3;
import com.carrotsearch.hppc.predicates.*;
import com.carrotsearch.hppc.procedures.*;
import static com.carrotsearch.hppc.Internals.*;
/**
* A hash map of double
to char
, implemented using open
* addressing with linear probing for collision resolution.
*
*
* The internal buffers of this implementation ({@link #keys}, {@link #values},
* {@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.
*
*
* See {@link ObjectObjectOpenHashMap} class for API similarities and differences against Java
* Collections.
*
*
*
Important node. The implementation uses power-of-two tables and linear
* probing, which may cause poor performance (many collisions) if hash values are
* not properly distributed. This implementation uses rehashing
* using {@link MurmurHash3}.
*
* @author This code is inspired by the collaboration and implementation in the fastutil project.
*/
@javax.annotation.Generated(date = "2011-07-12T16:58:51+0200", value = "HPPC generated from: DoubleCharOpenHashMap.java")
public class DoubleCharOpenHashMap
implements DoubleCharMap, Cloneable
{
/**
* Default capacity.
*/
public final static int DEFAULT_CAPACITY = 16;
/**
* Minimum capacity for the map.
*/
public final static int MIN_CAPACITY = 4;
/**
* Default load factor.
*/
public final static float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* Hash-indexed array holding all keys.
*
* @see #values
*/
public double [] keys;
/**
* Hash-indexed array holding all values associated to the keys
* stored in {@link #keys}.
*
* @see #keys
*/
public char [] values;
/**
* Information if an entry (slot) in the {@link #values} table is allocated
* or empty.
*
* @see #assigned
*/
public boolean [] allocated;
/**
* Cached number of assigned slots in {@link #allocated}.
*/
public int assigned;
/**
* The load factor for this map (fraction of allocated slots
* before the buffers must be rehashed or reallocated).
*/
public final float loadFactor;
/**
* Cached capacity threshold at which we must resize the buffers.
*/
private int resizeThreshold;
/**
* The most recent slot accessed in {@link #containsKey} (required for
* {@link #lget}).
*
* @see #containsKey
* @see #lget
*/
private int lastSlot;
/**
* Creates a hash map with the default capacity of {@value #DEFAULT_CAPACITY},
* load factor of {@value #DEFAULT_LOAD_FACTOR}.
*
* See class notes about hash distribution importance.
*/
public DoubleCharOpenHashMap()
{
this(DEFAULT_CAPACITY);
}
/**
* Creates a hash map with the given initial capacity, default load factor of
* {@value #DEFAULT_LOAD_FACTOR}.
*
* See class notes about hash distribution importance.
*
* @param initialCapacity Initial capacity (greater than zero and automatically
* rounded to the next power of two).
*/
public DoubleCharOpenHashMap(int initialCapacity)
{
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* Creates a hash map with the given initial capacity,
* load factor.
*
* See class notes about hash distribution importance.
*
* @param initialCapacity Initial capacity (greater than zero and automatically
* rounded to the next power of two).
*
* @param loadFactor The load factor (greater than zero and smaller than 1).
*/
public DoubleCharOpenHashMap(int initialCapacity, float loadFactor)
{
initialCapacity = Math.max(initialCapacity, MIN_CAPACITY);
assert initialCapacity > 0
: "Initial capacity must be between (0, " + Integer.MAX_VALUE + "].";
assert loadFactor > 0 && loadFactor <= 1
: "Load factor must be between (0, 1].";
this.loadFactor = loadFactor;
allocateBuffers(roundCapacity(initialCapacity));
}
/**
* Create a hash map from all key-value pairs of another container.
*/
public DoubleCharOpenHashMap(DoubleCharAssociativeContainer container)
{
this((int)(container.size() * (1 + DEFAULT_LOAD_FACTOR)));
putAll(container);
}
/**
* {@inheritDoc}
*/
@Override
public char put(double key, char value)
{
if (assigned >= resizeThreshold)
expandAndRehash();
final int mask = allocated.length - 1;
int slot = rehash(key) & mask;
while (allocated[slot])
{
if (((key) == (keys[slot])))
{
final char oldValue = values[slot];
values[slot] = value;
return oldValue;
}
slot = (slot + 1) & mask;
}
assigned++;
allocated[slot] = true;
keys[slot] = key;
values[slot] = value;
return ((char) 0);
}
/**
* {@inheritDoc}
*/
@Override
public final int putAll(
DoubleCharAssociativeContainer container)
{
final int count = this.assigned;
for (DoubleCharCursor c : container)
{
put(c.key, c.value);
}
return this.assigned - count;
}
/**
* Puts all key/value pairs from a given iterable into this map.
*/
@Override
public final int putAll(
Iterable iterable)
{
final int count = this.assigned;
for (DoubleCharCursor c : iterable)
{
put(c.key, c.value);
}
return this.assigned - count;
}
/**
* Trove-inspired API method. An equivalent
* of the following code:
*
* if (!map.containsKey(key)) map.put(value);
*
*
* @param key The key of the value to check.
* @param value The value to put if key
does not exist.
* @return true
if key
did not exist and value
* was placed in the map.
*/
public final boolean putIfAbsent(double key, char value)
{
if (!containsKey(key))
{
put(key, value);
return true;
}
return false;
}
/**
* Trove-inspired API method. An equivalent
* of the following code:
*
* if (map.containsKey(key))
* map.lset(map.lget() + additionValue);
* else
* map.put(key, 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).
*/
public final char putOrAdd(double key, char putValue, char additionValue)
{
if (assigned >= resizeThreshold)
expandAndRehash();
final int mask = allocated.length - 1;
int slot = rehash(key) & mask;
while (allocated[slot])
{
if (((key) == (keys[slot])))
{
return values[slot] += additionValue;
}
slot = (slot + 1) & mask;
}
assigned++;
allocated[slot] = true;
keys[slot] = key;
char v = values[slot] = putValue;
return v;
}
/**
* Expand the internal storage buffers (capacity) or rehash current
* keys and values if there are a lot of deleted slots.
*/
private void expandAndRehash()
{
final double [] oldKeys = this.keys;
final char [] oldValues = this.values;
final boolean [] oldStates = this.allocated;
assert assigned >= resizeThreshold;
allocateBuffers(nextCapacity(keys.length));
/*
* Rehash all assigned slots from the old hash table. Deleted
* slots are discarded.
*/
final int mask = allocated.length - 1;
for (int i = 0; i < oldStates.length; i++)
{
if (oldStates[i])
{
final double key = oldKeys[i];
final char value = oldValues[i];
/* */
/* */
int slot = rehash(key) & mask;
while (allocated[slot])
{
if (((key) == (keys[slot])))
{
break;
}
slot = (slot + 1) & mask;
}
allocated[slot] = true;
keys[slot] = key;
values[slot] = value;
}
}
/*
* The number of assigned items does not change, the number of deleted
* items is zero since we have resized.
*/
lastSlot = -1;
}
/**
* Allocate internal buffers for a given capacity.
*
* @param capacity New capacity (must be a power of two).
*/
private void allocateBuffers(int capacity)
{
this.keys = new double [capacity];
this.values = new char [capacity];
this.allocated = new boolean [capacity];
this.resizeThreshold = (int) (capacity * loadFactor);
}
/**
* {@inheritDoc}
*/
@Override
public char remove(double key)
{
final int mask = allocated.length - 1;
int slot = rehash(key) & mask;
while (allocated[slot])
{
if (((key) == (keys[slot])))
{
assigned--;
char v = values[slot];
shiftConflictingKeys(slot);
return v;
}
slot = (slot + 1) & mask;
}
return ((char) 0);
}
/**
* Shift all the slot-conflicting keys allocated to (and including) slot
.
*/
protected final void shiftConflictingKeys(int slotCurr)
{
// Copied nearly verbatim from fastutil's impl.
final int mask = allocated.length - 1;
int slotPrev, slotOther;
while (true)
{
slotCurr = ((slotPrev = slotCurr) + 1) & mask;
while (allocated[slotCurr])
{
slotOther = rehash(keys[slotCurr]) & mask;
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])
break;
// Shift key/value pair.
keys[slotPrev] = keys[slotCurr];
values[slotPrev] = values[slotCurr];
}
allocated[slotPrev] = false;
/* */
/* */
}
/**
* {@inheritDoc}
*/
@Override
public final int removeAll(DoubleContainer container)
{
final int before = this.assigned;
for (DoubleCursor cursor : container)
{
remove(cursor.value);
}
return before - this.assigned;
}
/**
* {@inheritDoc}
*/
@Override
public final int removeAll(DoublePredicate predicate)
{
final int before = this.assigned;
final double [] keys = this.keys;
final boolean [] states = this.allocated;
for (int i = 0; i < states.length;)
{
if (states[i])
{
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 char get(double key)
{
final int mask = allocated.length - 1;
int slot = rehash(key) & mask;
while (allocated[slot])
{
if (((key) == (keys[slot])))
{
return values[slot];
}
slot = (slot + 1) & mask;
}
return ((char) 0);
}
/**
* Returns the last value saved in a call to {@link #containsKey}.
*
* @see #containsKey
*/
public char lget()
{
assert lastSlot >= 0 : "Call containsKey() first.";
assert allocated[lastSlot] : "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.
*
* @see #containsKey
* @return Returns the previous value stored under the given key.
*/
public char lset(char key)
{
assert lastSlot >= 0 : "Call containsKey() first.";
assert allocated[lastSlot] : "Last call to exists did not have any associated value.";
final char previous = values[lastSlot];
values[lastSlot] = key;
return previous;
}
/**
* {@inheritDoc}
*
* Saves the associated value for fast access using {@link #lget}
* or {@link #lset}.
*
* if (map.containsKey(key))
* value = map.lget();
*
* or, for example 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(double key)
{
final int mask = allocated.length - 1;
int slot = rehash(key) & mask;
while (allocated[slot])
{
if (((key) == (keys[slot])))
{
lastSlot = slot;
return true;
}
slot = (slot + 1) & mask;
}
lastSlot = -1;
return false;
}
/**
* Round the capacity to the next allowed value.
*/
protected int roundCapacity(int requestedCapacity)
{
// Maximum positive integer that is a power of two.
if (requestedCapacity > (0x80000000 >>> 1))
return (0x80000000 >>> 1);
return Math.max(MIN_CAPACITY, BitUtil.nextHighestPowerOfTwo(requestedCapacity));
}
/**
* Return the next possible capacity, counting from the current buffers'
* size.
*/
protected int nextCapacity(int current)
{
assert current > 0 && Long.bitCount(current) == 1
: "Capacity must be a power of two.";
assert ((current << 1) > 0)
: "Maximum capacity exceeded (" + (0x80000000 >>> 1) + ").";
if (current < MIN_CAPACITY / 2) current = MIN_CAPACITY / 2;
return current << 1;
}
/**
* {@inheritDoc}
*
* Does not release internal buffers.
*/
@Override
public void clear()
{
assigned = 0;
// States are always cleared.
Arrays.fill(allocated, false);
/* */
/* */
}
/**
* {@inheritDoc}
*/
@Override
public int size()
{
return assigned;
}
/**
* {@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.
*/
public boolean isEmpty()
{
return size() == 0;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode()
{
int h = 0;
for (DoubleCharCursor c : this)
{
h += rehash(c.key) + rehash(c.value);
}
return h;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj)
{
if (obj != null)
{
if (obj == this) return true;
if (obj instanceof DoubleCharMap)
{
/* */
DoubleCharMap other = (DoubleCharMap) obj;
if (other.size() == this.size())
{
for (DoubleCharCursor c : this)
{
if (other.containsKey(c.key))
{
char v = other.get(c.key);
if (((c.value) == (v)))
{
continue;
}
}
return false;
}
return true;
}
}
}
return false;
}
/**
* An iterator implementation for {@link #iterator}.
*/
private final class EntryIterator extends AbstractIterator
{
private final DoubleCharCursor cursor;
public EntryIterator()
{
cursor = new DoubleCharCursor();
cursor.index = -1;
}
@Override
protected DoubleCharCursor fetch()
{
int i = cursor.index + 1;
final int max = keys.length;
while (i < max && !allocated[i])
{
i++;
}
if (i == max)
return done();
cursor.index = i;
cursor.key = keys[i];
cursor.value = values[i];
return cursor;
}
}
/**
* {@inheritDoc}
*/
@Override
public Iterator iterator()
{
return new EntryIterator();
}
/**
* {@inheritDoc}
*/
@Override
public T forEach(T procedure)
{
final double [] keys = this.keys;
final char [] values = this.values;
final boolean [] states = this.allocated;
for (int i = 0; i < states.length; i++)
{
if (states[i])
procedure.apply(keys[i], values[i]);
}
return procedure;
}
/**
* Returns a specialized view of the keys of this associated container.
* The view additionally implements {@link ObjectLookupContainer}.
*/
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 DoubleCharOpenHashMap owner =
DoubleCharOpenHashMap.this;
@Override
public boolean contains(double e)
{
return containsKey(e);
}
@Override
public T forEach(T procedure)
{
final double [] localKeys = owner.keys;
final boolean [] localStates = owner.allocated;
for (int i = 0; i < localStates.length; i++)
{
if (localStates[i])
procedure.apply(localKeys[i]);
}
return procedure;
}
@Override
public T forEach(T predicate)
{
final double [] localKeys = owner.keys;
final boolean [] localStates = owner.allocated;
for (int i = 0; i < localStates.length; i++)
{
if (localStates[i])
{
if (!predicate.apply(localKeys[i]))
break;
}
}
return predicate;
}
@Override
public boolean isEmpty()
{
return owner.isEmpty();
}
@Override
public Iterator iterator()
{
return new KeysIterator();
}
@Override
public int size()
{
return owner.size();
}
@Override
public void clear()
{
owner.clear();
}
@Override
public int removeAll(DoublePredicate predicate)
{
return owner.removeAll(predicate);
}
@Override
public int removeAllOccurrences(final double e)
{
final boolean hasKey = owner.containsKey(e);
int result = 0;
if (hasKey)
{
owner.remove(e);
result = 1;
}
return result;
}
};
/**
* An iterator over the set of assigned keys.
*/
private final class KeysIterator extends AbstractIterator
{
private final DoubleCursor cursor;
public KeysIterator()
{
cursor = new DoubleCursor();
cursor.index = -1;
}
@Override
protected DoubleCursor fetch()
{
int i = cursor.index + 1;
final int max = keys.length;
while (i < max && !allocated[i])
{
i++;
}
if (i == max)
return done();
cursor.index = i;
cursor.value = keys[i];
return cursor;
}
}
/**
* @return Returns a container with all values stored in this map.
*/
@Override
public CharContainer values()
{
return new ValuesContainer();
}
/**
* A view over the set of values of this map.
*/
private final class ValuesContainer extends AbstractCharCollection
{
@Override
public int size()
{
return DoubleCharOpenHashMap.this.size();
}
@Override
public boolean isEmpty()
{
return DoubleCharOpenHashMap.this.isEmpty();
}
@Override
public boolean contains(char value)
{
// This is a linear scan over the values, but it's in the contract, so be it.
final boolean [] allocated = DoubleCharOpenHashMap.this.allocated;
final char [] values = DoubleCharOpenHashMap.this.values;
for (int slot = 0; slot < allocated.length; slot++)
{
if (allocated[slot] && ((value) == (values[slot])))
{
return true;
}
}
return false;
}
@Override
public T forEach(T procedure)
{
final boolean [] allocated = DoubleCharOpenHashMap.this.allocated;
final char [] values = DoubleCharOpenHashMap.this.values;
for (int i = 0; i < allocated.length; i++)
{
if (allocated[i])
procedure.apply(values[i]);
}
return procedure;
}
@Override
public T forEach(T predicate)
{
final boolean [] allocated = DoubleCharOpenHashMap.this.allocated;
final char [] values = DoubleCharOpenHashMap.this.values;
for (int i = 0; i < allocated.length; i++)
{
if (allocated[i])
{
if (!predicate.apply(values[i]))
break;
}
}
return predicate;
}
@Override
public Iterator iterator()
{
return new ValuesIterator();
}
@Override
public int removeAllOccurrences(char e)
{
throw new UnsupportedOperationException();
}
@Override
public int removeAll(CharPredicate predicate)
{
throw new UnsupportedOperationException();
}
@Override
public void clear()
{
throw new UnsupportedOperationException();
}
}
/**
* An iterator over the set of assigned values.
*/
private final class ValuesIterator extends AbstractIterator
{
private final CharCursor cursor;
public ValuesIterator()
{
cursor = new CharCursor();
cursor.index = -1;
}
@Override
protected CharCursor fetch()
{
int i = cursor.index + 1;
final int max = keys.length;
while (i < max && !allocated[i])
{
i++;
}
if (i == max)
return done();
cursor.index = i;
cursor.value = values[i];
return cursor;
}
}
/**
* {@inheritDoc}
*/
@Override
public DoubleCharOpenHashMap clone()
{
try
{
/* */
DoubleCharOpenHashMap cloned =
(DoubleCharOpenHashMap) super.clone();
cloned.keys = keys.clone();
cloned.values = values.clone();
cloned.allocated = allocated.clone();
return cloned;
}
catch (CloneNotSupportedException e)
{
throw new RuntimeException(e);
}
}
/**
* 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 (DoubleCharCursor 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 DoubleCharOpenHashMap from(double [] keys, char [] values)
{
if (keys.length != values.length)
throw new IllegalArgumentException("Arrays of keys and values must have an identical length.");
DoubleCharOpenHashMap map = new DoubleCharOpenHashMap();
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 DoubleCharOpenHashMap from(DoubleCharAssociativeContainer container)
{
return new DoubleCharOpenHashMap(container);
}
/**
* Create a new hash map without providing the full generic signature (constructor
* shortcut).
*/
public static DoubleCharOpenHashMap newInstance()
{
return new DoubleCharOpenHashMap();
}
/**
* Create a new hash map without providing the full generic signature (constructor
* shortcut).
*/
public static DoubleCharOpenHashMap newInstance(int initialCapacity, float loadFactor)
{
return new DoubleCharOpenHashMap(initialCapacity, loadFactor);
}
}