int
to Object
, implemented using open addressing with
* linear probing for collision resolution. Supports null values.
*
* @see HPPC interfaces diagram
*/
@SuppressWarnings("unchecked")
@com.carrotsearch.hppc.Generated(
date = "2024-06-04T15:20:16+0200",
value = "KTypeVTypeHashMap.java")
public class IntObjectHashMapThe output from this function should evenly distribute keys across the entire integer range. */ protected int hashKey(int 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(int[] fromKeys, VType[] fromValues) { assert fromKeys.length == fromValues.length && HashContainers.checkPowerOfTwo(fromKeys.length - 1); // Rehash all stored key/value pairs into the new buffers. final int[] keys = this.keys; final VType[] values = (VType[]) this.values; final int mask = this.mask; int 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. int[] prevKeys = this.keys; VType[] prevValues = (VType[]) this.values; try { int emptyElementSlot = 1; this.keys = (new int[arraySize + emptyElementSlot]); this.values = ((VType[]) new Object[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, int pendingKey, VType 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 int[] prevKeys = this.keys;
final VType[] prevValues = (VType[]) 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 int[] keys = this.keys;
final VType[] values = (VType[]) 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 int 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] = 0;
values[gapSlot] = null;
assigned--;
}
}