com.carrotsearch.hppc.ShortLongHashMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hppc Show documentation
Show all versions of hppc Show documentation
High Performance Primitive Collections: data structures (maps, sets, lists, stacks, queues) generated for combinations of object and primitive types to conserve JVM memory and speed up execution.
package com.carrotsearch.hppc;
import java.util.*;
import com.carrotsearch.hppc.cursors.*;
import com.carrotsearch.hppc.predicates.*;
import com.carrotsearch.hppc.procedures.*;
import static com.carrotsearch.hppc.HashContainers.*;
import static com.carrotsearch.hppc.Containers.*;
/**
* A hash map of short
to long
, implemented using open
* addressing with linear probing for collision resolution.
*
* @see HPPC interfaces diagram
*/
@com.carrotsearch.hppc.Generated(
date = "2021-06-08T13:12:53+0200",
value = "KTypeVTypeHashMap.java")
public class ShortLongHashMap
implements
ShortLongMap,
Preallocable,
Cloneable,
Accountable
{
/**
* The array holding keys.
*/
public short []
keys;
/**
* The array holding values.
*/
public long []
values;
/**
* The number of stored keys (assigned key slots), excluding the special
* "empty" key, if any (use {@link #size()} instead).
*
* @see #size()
*/
protected int assigned;
/**
* Mask for slot scans in {@link #keys}.
*/
protected int mask;
/**
* Expand (rehash) {@link #keys} when {@link #assigned} hits this value.
*/
protected int resizeAt;
/**
* Special treatment for the "empty slot" key marker.
*/
protected boolean hasEmptyKey;
/**
* The load factor for {@link #keys}.
*/
protected double loadFactor;
/**
* Seed used to ensure the hash iteration order is different from an iteration to another.
*/
protected int iterationSeed;
/**
* New instance with sane defaults.
*/
public ShortLongHashMap() {
this(DEFAULT_EXPECTED_ELEMENTS);
}
/**
* New instance with sane defaults.
*
* @param expectedElements
* The expected number of elements guaranteed not to cause buffer
* expansion (inclusive).
*/
public ShortLongHashMap(int expectedElements) {
this(expectedElements, DEFAULT_LOAD_FACTOR);
}
/**
* New instance with the provided defaults.
*
* @param expectedElements
* The expected number of elements guaranteed not to cause a rehash (inclusive).
* @param loadFactor
* The load factor for internal buffers. Insane load factors (zero, full capacity)
* are rejected by {@link #verifyLoadFactor(double)}.
*/
public ShortLongHashMap(int expectedElements, double loadFactor) {
this.loadFactor = verifyLoadFactor(loadFactor);
iterationSeed = HashContainers.nextIterationSeed();
ensureCapacity(expectedElements);
}
/**
* Create a hash map from all key-value pairs of another container.
*/
public ShortLongHashMap(ShortLongAssociativeContainer container) {
this(container.size());
putAll(container);
}
/**
* {@inheritDoc}
*/
@Override
public long put(short key, long value) {
assert assigned < mask + 1;
final int mask = this.mask;
if (((key) == 0)) {
hasEmptyKey = true;
long previousValue = values[mask + 1];
values[mask + 1] = value;
return previousValue;
} else {
final short[] keys = this.keys;
int slot = hashKey(key) & mask;
short existing;
while (!((existing = keys[slot]) == 0)) {
if (((existing) == ( key))) {
final long previousValue = values[slot];
values[slot] = value;
return previousValue;
}
slot = (slot + 1) & mask;
}
if (assigned == resizeAt) {
allocateThenInsertThenRehash(slot, key, value);
} else {
keys[slot] = key;
values[slot] = value;
}
assigned++;
return 0L;
}
}
/**
* {@inheritDoc}
*/
@Override
public int putAll(ShortLongAssociativeContainer container) {
final int count = size();
for (ShortLongCursor c : container) {
put(c.key, c.value);
}
return size() - count;
}
/**
* Puts all key/value pairs from a given iterable into this map.
*/
@Override
public int putAll(Iterable extends ShortLongCursor> iterable){
final int count = size();
for (ShortLongCursor c : iterable) {
put(c.key, c.value);
}
return size() - 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 boolean putIfAbsent(short key, long value) {
int keyIndex = indexOf(key);
if (!indexExists(keyIndex)) {
indexInsert(keyIndex, key, value);
return true;
} else {
return false;
}
}
/**
* If key
exists, putValue
is inserted into the map,
* otherwise any existing value is incremented by additionValue
.
*
* @param key
* The key of the value to adjust.
* @param putValue
* The value to put if key
does not exist.
* @param incrementValue
* The value to add to the existing value if key
exists.
* @return Returns the current value associated with key
(after
* changes).
*/
@Override
public long putOrAdd(short key, long putValue, long incrementValue) {
assert assigned < mask + 1;
int keyIndex = indexOf(key);
if (indexExists(keyIndex)) {
putValue = ((long) (( values[keyIndex]) + (incrementValue)));
indexReplace(keyIndex, putValue);
} else {
indexInsert(keyIndex, key, putValue);
}
return putValue;
}
/**
* Adds incrementValue
to any existing value for the given key
* or inserts incrementValue
if key
did not previously exist.
*
* @param key The key of the value to adjust.
* @param incrementValue The value to put or add to the existing value if key
exists.
* @return Returns the current value associated with key
(after changes).
*/
@Override
public long addTo(short key, long incrementValue)
{
return putOrAdd(key, incrementValue, incrementValue);
}
/**
* {@inheritDoc}
*/
@Override
public long remove(short key) {
final int mask = this.mask;
if (((key) == 0)) {
hasEmptyKey = false;
long previousValue = values[mask + 1];
values[mask + 1] = 0L;
return previousValue;
} else {
final short[] keys = this.keys;
int slot = hashKey(key) & mask;
short existing;
while (!((existing = keys[slot]) == 0)) {
if (((existing) == ( key))) {
final long previousValue = values[slot];
shiftConflictingKeys(slot);
return previousValue;
}
slot = (slot + 1) & mask;
}
return 0L;
}
}
/**
* {@inheritDoc}
*/
@Override
public int removeAll(ShortContainer other) {
final int before = size();
// Try to iterate over the smaller set of values or
// over the container that isn't implementing
// efficient contains() lookup.
if (other.size() >= size() &&
other instanceof ShortLookupContainer) {
if (hasEmptyKey && other.contains(((short) 0))) {
hasEmptyKey = false;
values[mask + 1] = 0L;
}
final short[] keys = this.keys;
for (int slot = 0, max = this.mask; slot <= max;) {
short existing;
if (!((existing = keys[slot]) == 0) && other.contains(existing)) {
// Shift, do not increment slot.
shiftConflictingKeys(slot);
} else {
slot++;
}
}
} else {
for (ShortCursor c : other) {
remove( c.value);
}
}
return before - size();
}
/**
* {@inheritDoc}
*/
@Override
public int removeAll(ShortLongPredicate predicate) {
final int before = size();
final int mask = this.mask;
if (hasEmptyKey) {
if (predicate.apply(((short) 0), values[mask + 1])) {
hasEmptyKey = false;
values[mask + 1] = 0L;
}
}
final short[] keys = this.keys;
final long[] values = this.values;
for (int slot = 0; slot <= mask;) {
short existing;
if (!((existing = keys[slot]) == 0) &&
predicate.apply(existing, values[slot])) {
// Shift, do not increment slot.
shiftConflictingKeys(slot);
} else {
slot++;
}
}
return before - size();
}
/**
* {@inheritDoc}
*/
@Override
public int removeAll(ShortPredicate predicate) {
final int before = size();
if (hasEmptyKey) {
if (predicate.apply(((short) 0))) {
hasEmptyKey = false;
values[mask + 1] = 0L;
}
}
final short[] keys = this.keys;
for (int slot = 0, max = this.mask; slot <= max;) {
short existing;
if (!((existing = keys[slot]) == 0) &&
predicate.apply(existing)) {
// Shift, do not increment slot.
shiftConflictingKeys(slot);
} else {
slot++;
}
}
return before - size();
}
/**
* {@inheritDoc}
*/
@Override
public long get(short key) {
if (((key) == 0)) {
return hasEmptyKey ? values[mask + 1] : 0L;
} else {
final short[] keys = this.keys;
final int mask = this.mask;
int slot = hashKey(key) & mask;
short existing;
while (!((existing = keys[slot]) == 0)) {
if (((existing) == ( key))) {
return values[slot];
}
slot = (slot + 1) & mask;
}
return 0L;
}
}
/**
* {@inheritDoc}
*/
@Override
public long getOrDefault(short key, long defaultValue) {
if (((key) == 0)) {
return hasEmptyKey ? values[mask + 1] : defaultValue;
} else {
final short[] keys = this.keys;
final int mask = this.mask;
int slot = hashKey(key) & mask;
short existing;
while (!((existing = keys[slot]) == 0)) {
if (((existing) == ( key))) {
return values[slot];
}
slot = (slot + 1) & mask;
}
return defaultValue;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsKey(short key) {
if (((key) == 0)) {
return hasEmptyKey;
} else {
final short[] keys = this.keys;
final int mask = this.mask;
int slot = hashKey(key) & mask;
short existing;
while (!((existing = keys[slot]) == 0)) {
if (((existing) == ( key))) {
return true;
}
slot = (slot + 1) & mask;
}
return false;
}
}
/**
* {@inheritDoc}
*/
@Override
public int indexOf(short key) {
final int mask = this.mask;
if (((key) == 0)) {
return hasEmptyKey ? mask + 1 : ~(mask + 1);
} else {
final short[] keys = this.keys;
int slot = hashKey(key) & mask;
short existing;
while (!((existing = keys[slot]) == 0)) {
if (((existing) == ( key))) {
return slot;
}
slot = (slot + 1) & mask;
}
return ~slot;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean indexExists(int index) {
assert index < 0 ||
(index >= 0 && index <= mask) ||
(index == mask + 1 && hasEmptyKey);
return index >= 0;
}
/**
* {@inheritDoc}
*/
@Override
public long indexGet(int index) {
assert index >= 0 : "The index must point at an existing key.";
assert index <= mask ||
(index == mask + 1 && hasEmptyKey);
return values[index];
}
/**
* {@inheritDoc}
*/
@Override
public long indexReplace(int index, long newValue) {
assert index >= 0 : "The index must point at an existing key.";
assert index <= mask ||
(index == mask + 1 && hasEmptyKey);
long previousValue = values[index];
values[index] = newValue;
return previousValue;
}
/**
* {@inheritDoc}
*/
@Override
public void indexInsert(int index, short key, long value) {
assert index < 0 : "The index must not point at an existing key.";
index = ~index;
if (((key) == 0)) {
assert index == mask + 1;
values[index] = value;
hasEmptyKey = true;
} else {
assert ((keys[index]) == 0);
if (assigned == resizeAt) {
allocateThenInsertThenRehash(index, key, value);
} else {
keys[index] = key;
values[index] = value;
}
assigned++;
}
}
/**
* {@inheritDoc}
*/
@Override
public long indexRemove(int index) {
assert index >= 0 : "The index must point at an existing key.";
assert index <= mask ||
(index == mask + 1 && hasEmptyKey);
long previousValue = values[index];
if (index > mask) {
hasEmptyKey = false;
values[index] = 0L;
} else {
shiftConflictingKeys(index);
}
return previousValue;
}
/**
* {@inheritDoc}
*/
@Override
public void clear() {
assigned = 0;
hasEmptyKey = false;
Arrays.fill(keys, ((short) 0));
/* */
}
/**
* {@inheritDoc}
*/
@Override
public void release() {
assigned = 0;
hasEmptyKey = false;
keys = null;
values = null;
ensureCapacity(Containers.DEFAULT_EXPECTED_ELEMENTS);
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
return assigned + (hasEmptyKey ? 1 : 0);
}
/**
* {@inheritDoc}
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
int h = hasEmptyKey ? 0xDEADBEEF : 0;
for (ShortLongCursor c : this) {
h += BitMixer.mix(c.key) +
BitMixer.mix(c.value);
}
return h;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
return obj != null &&
getClass() == obj.getClass() &&
equalElements(getClass().cast(obj));
}
/**
* Return true if all keys of some other container exist in this container.
*/
protected boolean equalElements(ShortLongHashMap other) {
if (other.size() != size()) {
return false;
}
for (ShortLongCursor c : other) {
short key = c.key;
if (!containsKey(key) ||
!((get(key)) == (c.value))) {
return false;
}
}
return true;
}
/**
* Ensure this container can hold at least the
* given number of keys (entries) without resizing its buffers.
*
* @param expectedElements The total number of keys, inclusive.
*/
@Override
public void ensureCapacity(int expectedElements) {
if (expectedElements > resizeAt || keys == null) {
final short[] prevKeys = this.keys;
final long[] prevValues = this.values;
allocateBuffers(minBufferSize(expectedElements, loadFactor));
if (prevKeys != null && !isEmpty()) {
rehash(prevKeys, prevValues);
}
}
}
@Override
public long ramBytesAllocated() {
// int: iterationSeed, assigned, mask, resizeAt
// double: loadFactor
// boolean: hasEmptyKey
return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER + 4 * Integer.BYTES + Double.BYTES + 1 +
RamUsageEstimator.shallowSizeOfArray(keys) + RamUsageEstimator.shallowSizeOfArray(values);
}
@Override
public long ramBytesUsed() {
// int: iterationSeed, assigned, mask, resizeAt
// double: loadFactor
// boolean: hasEmptyKey
return RamUsageEstimator.NUM_BYTES_OBJECT_HEADER + 4 * Integer.BYTES + Double.BYTES + 1 +
RamUsageEstimator.shallowUsedSizeOfArray(keys, size()) +
RamUsageEstimator.shallowUsedSizeOfArray(values, size());
}
/**
* Provides the next iteration seed used to build the iteration starting slot and offset increment.
* This method does not need to be synchronized, what matters is that each thread gets a sequence of varying seeds.
*/
protected int nextIterationSeed() {
return iterationSeed = BitMixer.mixPhi(iterationSeed);
}
/**
* An iterator implementation for {@link #iterator}.
*/
private final class EntryIterator extends AbstractIterator {
private final ShortLongCursor cursor;
private final int increment;
private int index;
private int slot;
public EntryIterator() {
cursor = new ShortLongCursor();
int seed = nextIterationSeed();
increment = iterationIncrement(seed);
slot = seed & mask;
}
@Override
protected ShortLongCursor fetch() {
final int mask = ShortLongHashMap.this.mask;
while (index <= mask) {
short existing;
index++;
slot = (slot + increment) & mask;
if (!((existing = keys[slot]) == 0)) {
cursor.index = slot;
cursor.key = existing;
cursor.value = values[slot];
return cursor;
}
}
if (index == mask + 1 && hasEmptyKey) {
cursor.index = index;
cursor.key = ((short) 0);
cursor.value = values[index++];
return cursor;
}
return done();
}
}
/**
* {@inheritDoc}
*/
@Override
public Iterator iterator() {
return new EntryIterator();
}
/**
* {@inheritDoc}
*/
@Override
public T forEach(T procedure) {
final short[] keys = this.keys;
final long[] values = this.values;
if (hasEmptyKey) {
procedure.apply(((short) 0), values[mask + 1]);
}
int seed = nextIterationSeed();
int inc = iterationIncrement(seed);
for (int i = 0, mask = this.mask, slot = seed & mask; i <= mask; i++, slot = (slot + inc) & mask) {
if (!((keys[slot]) == 0)) {
procedure.apply(keys[slot], values[slot]);
}
}
return procedure;
}
/**
* {@inheritDoc}
*/
@Override
public T forEach(T predicate) {
final short[] keys = this.keys;
final long[] values = this.values;
if (hasEmptyKey) {
if (!predicate.apply(((short) 0), values[mask + 1])) {
return predicate;
}
}
int seed = nextIterationSeed();
int inc = iterationIncrement(seed);
for (int i = 0, mask = this.mask, slot = seed & mask; i <= mask; i++, slot = (slot + inc) & mask) {
if (!((keys[slot]) == 0)) {
if (!predicate.apply(keys[slot], values[slot])) {
break;
}
}
}
return predicate;
}
/**
* 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 AbstractShortCollection
implements ShortLookupContainer {
private final ShortLongHashMap owner = ShortLongHashMap.this;
@Override
public boolean contains(short e) {
return owner.containsKey(e);
}
@Override
public T forEach(final T procedure) {
owner.forEach((ShortLongProcedure) (k, v) -> procedure.apply(k));
return procedure;
}
@Override
public T forEach(final T predicate) {
owner.forEach((ShortLongPredicate) (key, value) -> predicate.apply(key));
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 void release() {
owner.release();
}
@Override
public int removeAll(ShortPredicate predicate) {
return owner.removeAll(predicate);
}
@Override
public int removeAll(final short e) {
if (owner.containsKey(e)) {
owner.remove(e);
return 1;
} else {
return 0;
}
}
};
/**
* An iterator over the set of assigned keys.
*/
private final class KeysIterator extends AbstractIterator {
private final ShortCursor cursor;
private final int increment;
private int index;
private int slot;
public KeysIterator() {
cursor = new ShortCursor();
int seed = nextIterationSeed();
increment = iterationIncrement(seed);
slot = seed & mask;
}
@Override
protected ShortCursor fetch() {
final int mask = ShortLongHashMap.this.mask;
while (index <= mask) {
short existing;
index++;
slot = (slot + increment) & mask;
if (!((existing = keys[slot]) == 0)) {
cursor.index = slot;
cursor.value = existing;
return cursor;
}
}
if (index == mask + 1 && hasEmptyKey) {
cursor.index = index++;
cursor.value = ((short) 0);
return cursor;
}
return done();
}
}
/**
* @return Returns a container with all values stored in this map.
*/
@Override
public LongCollection values() {
return new ValuesContainer();
}
/**
* A view over the set of values of this map.
*/
private final class ValuesContainer extends AbstractLongCollection {
private final ShortLongHashMap owner = ShortLongHashMap.this;
@Override
public int size() {
return owner.size();
}
@Override
public boolean isEmpty() {
return owner.isEmpty();
}
@Override
public boolean contains(long value) {
for (ShortLongCursor c : owner) {
if (((c.value) == (value))) {
return true;
}
}
return false;
}
@Override
public T forEach(T procedure) {
for (ShortLongCursor c : owner) {
procedure.apply(c.value);
}
return procedure;
}
@Override
public T forEach(T predicate) {
for (ShortLongCursor c : owner) {
if (!predicate.apply(c.value)) {
break;
}
}
return predicate;
}
@Override
public Iterator iterator() {
return new ValuesIterator();
}
@Override
public int removeAll(final long e) {
return owner.removeAll((key, value) -> ((value) == (e)));
}
@Override
public int removeAll(final LongPredicate predicate) {
return owner.removeAll((key, value) -> predicate.apply(value));
}
@Override
public void clear() {
owner.clear();
}
@Override
public void release() {
owner.release();
}
}
/**
* An iterator over the set of assigned values.
*/
private final class ValuesIterator extends AbstractIterator {
private final LongCursor cursor;
private final int increment;
private int index;
private int slot;
public ValuesIterator() {
cursor = new LongCursor();
int seed = nextIterationSeed();
increment = iterationIncrement(seed);
slot = seed & mask;
}
@Override
protected LongCursor fetch() {
final int mask = ShortLongHashMap.this.mask;
while (index <= mask) {
index++;
slot = (slot + increment) & mask;
if (!(( keys[slot]) == 0)) {
cursor.index = slot;
cursor.value = values[slot];
return cursor;
}
}
if (index == mask + 1 && hasEmptyKey) {
cursor.index = index;
cursor.value = values[index++];
return cursor;
}
return done();
}
}
/**
* {@inheritDoc}
*/
@Override
public ShortLongHashMap clone() {
try {
/* */
ShortLongHashMap cloned = (ShortLongHashMap) super.clone();
cloned.keys = keys.clone();
cloned.values = values.clone();
cloned.hasEmptyKey = hasEmptyKey;
cloned.iterationSeed = HashContainers.nextIterationSeed();
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 (ShortLongCursor cursor : this) {
if (!first) {
buffer.append(", ");
}
buffer.append(cursor.key);
buffer.append("=>");
buffer.append(cursor.value);
first = false;
}
buffer.append("]");
return buffer.toString();
}
@Override
public String visualizeKeyDistribution(int characters) {
return ShortBufferVisualizer.visualizeKeyDistribution(keys, mask, characters);
}
/**
* Creates a hash map from two index-aligned arrays of key-value pairs.
*/
public static ShortLongHashMap from(short[] keys, long[] values) {
if (keys.length != values.length) {
throw new IllegalArgumentException("Arrays of keys and values must have an identical length.");
}
ShortLongHashMap map = new ShortLongHashMap(keys.length);
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
return map;
}
/**
* Returns a hash code for the given key.
*
* The output from this function should evenly distribute keys across the
* entire integer range.
*/
protected
int hashKey(short key) {
assert !((key) == 0); // Handled as a special case (empty slot marker).
return BitMixer.mixPhi(key);
}
/**
* Validate load factor range and return it. Override and suppress if you need
* insane load factors.
*/
protected double verifyLoadFactor(double loadFactor) {
checkLoadFactor(loadFactor, MIN_LOAD_FACTOR, MAX_LOAD_FACTOR);
return loadFactor;
}
/**
* Rehash from old buffers to new buffers.
*/
protected void rehash(short[] fromKeys, long[] fromValues) {
assert fromKeys.length == fromValues.length &&
HashContainers.checkPowerOfTwo(fromKeys.length - 1);
// Rehash all stored key/value pairs into the new buffers.
final short[] keys = this.keys;
final long[] values = this.values;
final int mask = this.mask;
short existing;
// Copy the zero element's slot, then rehash everything else.
int from = fromKeys.length - 1;
keys[keys.length - 1] = fromKeys[from];
values[values.length - 1] = fromValues[from];
while (--from >= 0) {
if (!((existing = fromKeys[from]) == 0)) {
int slot = hashKey(existing) & mask;
while (!((keys[slot]) == 0)) {
slot = (slot + 1) & mask;
}
keys[slot] = existing;
values[slot] = fromValues[from];
}
}
}
/**
* Allocate new internal buffers. This method attempts to allocate
* and assign internal buffers atomically (either allocations succeed or not).
*/
protected void allocateBuffers(int arraySize) {
assert Integer.bitCount(arraySize) == 1;
// Ensure no change is done if we hit an OOM.
short[] prevKeys = this.keys;
long[] prevValues = this.values;
try {
int emptyElementSlot = 1;
this.keys = (new short [arraySize + emptyElementSlot]);
this.values = (new long [arraySize + emptyElementSlot]);
} catch (OutOfMemoryError e) {
this.keys = prevKeys;
this.values = prevValues;
throw new BufferAllocationException(
"Not enough memory to allocate buffers for rehashing: %,d -> %,d",
e,
this.mask + 1,
arraySize);
}
this.resizeAt = expandAtCount(arraySize, loadFactor);
this.mask = arraySize - 1;
}
/**
* This method is invoked when there is a new key/ value pair to be inserted into
* the buffers but there is not enough empty slots to do so.
*
* New buffers are allocated. If this succeeds, we know we can proceed
* with rehashing so we assign the pending element to the previous buffer
* (possibly violating the invariant of having at least one empty slot)
* and rehash all keys, substituting new buffers at the end.
*/
protected void allocateThenInsertThenRehash(int slot, short pendingKey, long pendingValue) {
assert assigned == resizeAt
&& (( keys[slot]) == 0)
&& !((pendingKey) == 0);
// Try to allocate new buffers first. If we OOM, we leave in a consistent state.
final short[] prevKeys = this.keys;
final long[] prevValues = this.values;
allocateBuffers(nextBufferSize(mask + 1, size(), loadFactor));
assert this.keys.length > prevKeys.length;
// We have succeeded at allocating new data so insert the pending key/value at
// the free slot in the old arrays before rehashing.
prevKeys[slot] = pendingKey;
prevValues[slot] = pendingValue;
// Rehash old keys, including the pending key.
rehash(prevKeys, prevValues);
}
/**
* Shift all the slot-conflicting keys and values allocated to
* (and including) slot
.
*/
protected void shiftConflictingKeys(int gapSlot) {
final short[] keys = this.keys;
final long[] values = this.values;
final int mask = this.mask;
// Perform shifts of conflicting keys to fill in the gap.
int distance = 0;
while (true) {
final int slot = (gapSlot + (++distance)) & mask;
final short existing = keys[slot];
if (((existing) == 0)) {
break;
}
final int idealSlot = hashKey(existing);
final int shift = (slot - idealSlot) & mask;
if (shift >= distance) {
// Entry at this position was originally at or before the gap slot.
// Move the conflict-shifted entry to the gap's position and repeat the procedure
// for any entries to the right of the current position, treating it
// as the new gap.
keys[gapSlot] = existing;
values[gapSlot] = values[slot];
gapSlot = slot;
distance = 0;
}
}
// Mark the last found gap slot without a conflict as empty.
keys[gapSlot] = ((short) 0);
values[gapSlot] = 0L;
assigned--;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy