Object
to int
, implemented using open addressing with
* linear probing for collision resolution. Supports null key.
*
* @see HPPC interfaces diagram
*/
@SuppressWarnings("unchecked")
@com.carrotsearch.hppc.Generated(
date = "2024-06-04T15:20:16+0200",
value = "KTypeVTypeHashMap.java")
public class ObjectIntHashMapkey
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 int putOrAdd(KType key, int putValue, int incrementValue) {
assert assigned < mask + 1;
int keyIndex = indexOf(key);
if (indexExists(keyIndex)) {
putValue = ((int) ((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 int addTo(KType key, int incrementValue) {
return putOrAdd(key, incrementValue, incrementValue);
}
/** {@inheritDoc} */
@Override
public int remove(KType key) {
final int mask = this.mask;
if (((key) == null)) {
if (!hasEmptyKey) {
return 0;
}
hasEmptyKey = false;
int previousValue = values[mask + 1];
values[mask + 1] = 0;
return previousValue;
} else {
final KType[] keys = (KType[]) this.keys;
int slot = hashKey(key) & mask;
KType existing;
while (!((existing = keys[slot]) == null)) {
if (this.equals(key, existing)) {
final int previousValue = values[slot];
shiftConflictingKeys(slot);
return previousValue;
}
slot = (slot + 1) & mask;
}
return 0;
}
}
/** {@inheritDoc} */
@Override
public int removeAll(ObjectContainer super KType> 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 ObjectLookupContainer>) {
if (hasEmptyKey && other.contains(null)) {
hasEmptyKey = false;
values[mask + 1] = 0;
}
final KType[] keys = (KType[]) this.keys;
for (int slot = 0, max = this.mask; slot <= max; ) {
KType existing;
if (!((existing = keys[slot]) == null) && other.contains(existing)) {
// Shift, do not increment slot.
shiftConflictingKeys(slot);
} else {
slot++;
}
}
} else {
for (ObjectCursor> c : other) {
remove((KType) c.value);
}
}
return before - size();
}
/** {@inheritDoc} */
@Override
public int removeAll(ObjectIntPredicate super KType> predicate) {
final int before = size();
final int mask = this.mask;
if (hasEmptyKey) {
if (predicate.apply(null, values[mask + 1])) {
hasEmptyKey = false;
values[mask + 1] = 0;
}
}
final KType[] keys = (KType[]) this.keys;
final int[] values = this.values;
for (int slot = 0; slot <= mask; ) {
KType existing;
if (!((existing = keys[slot]) == null) && predicate.apply(existing, values[slot])) {
// Shift, do not increment slot.
shiftConflictingKeys(slot);
} else {
slot++;
}
}
return before - size();
}
/** {@inheritDoc} */
@Override
public int removeAll(ObjectPredicate super KType> predicate) {
final int before = size();
if (hasEmptyKey) {
if (predicate.apply(null)) {
hasEmptyKey = false;
values[mask + 1] = 0;
}
}
final KType[] keys = (KType[]) this.keys;
for (int slot = 0, max = this.mask; slot <= max; ) {
KType existing;
if (!((existing = keys[slot]) == null) && predicate.apply(existing)) {
// Shift, do not increment slot.
shiftConflictingKeys(slot);
} else {
slot++;
}
}
return before - size();
}
/** {@inheritDoc} */
@Override
public int get(KType key) {
if (((key) == null)) {
return hasEmptyKey ? values[mask + 1] : 0;
} else {
final KType[] keys = (KType[]) this.keys;
final int mask = this.mask;
int slot = hashKey(key) & mask;
KType existing;
while (!((existing = keys[slot]) == null)) {
if (this.equals(key, existing)) {
return values[slot];
}
slot = (slot + 1) & mask;
}
return 0;
}
}
/** {@inheritDoc} */
@Override
public int getOrDefault(KType key, int defaultValue) {
if (((key) == null)) {
return hasEmptyKey ? values[mask + 1] : defaultValue;
} else {
final KType[] keys = (KType[]) this.keys;
final int mask = this.mask;
int slot = hashKey(key) & mask;
KType existing;
while (!((existing = keys[slot]) == null)) {
if (this.equals(key, existing)) {
return values[slot];
}
slot = (slot + 1) & mask;
}
return defaultValue;
}
}
/** {@inheritDoc} */
@Override
public boolean containsKey(KType key) {
if (((key) == null)) {
return hasEmptyKey;
} else {
final KType[] keys = (KType[]) this.keys;
final int mask = this.mask;
int slot = hashKey(key) & mask;
KType existing;
while (!((existing = keys[slot]) == null)) {
if (this.equals(key, existing)) {
return true;
}
slot = (slot + 1) & mask;
}
return false;
}
}
/** {@inheritDoc} */
@Override
public int indexOf(KType key) {
final int mask = this.mask;
if (((key) == null)) {
return hasEmptyKey ? mask + 1 : ~(mask + 1);
} else {
final KType[] keys = (KType[]) this.keys;
int slot = hashKey(key) & mask;
KType existing;
while (!((existing = keys[slot]) == null)) {
if (this.equals(key, existing)) {
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 int 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 int indexReplace(int index, int newValue) {
assert index >= 0 : "The index must point at an existing key.";
assert index <= mask || (index == mask + 1 && hasEmptyKey);
int previousValue = values[index];
values[index] = newValue;
return previousValue;
}
/** {@inheritDoc} */
@Override
public void indexInsert(int index, KType key, int value) {
assert index < 0 : "The index must not point at an existing key.";
index = ~index;
if (((key) == null)) {
assert index == mask + 1;
values[index] = value;
hasEmptyKey = true;
} else {
assert ((keys[index]) == null);
if (assigned == resizeAt) {
allocateThenInsertThenRehash(index, key, value);
} else {
keys[index] = key;
values[index] = value;
}
assigned++;
}
}
/** {@inheritDoc} */
@Override
public int indexRemove(int index) {
assert index >= 0 : "The index must point at an existing key.";
assert index <= mask || (index == mask + 1 && hasEmptyKey);
int previousValue = values[index];
if (index > mask) {
assert index == mask + 1;
hasEmptyKey = false;
values[index] = 0;
} else {
shiftConflictingKeys(index);
}
return previousValue;
}
/** {@inheritDoc} */
@Override
public void clear() {
assigned = 0;
hasEmptyKey = false;
Arrays.fill(keys, null);
}
/** {@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 (ObjectIntCursorThe output from this function should evenly distribute keys across the entire integer range. */ protected int hashKey(KType key) { assert !((key) == null); // 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(KType[] fromKeys, int[] fromValues) { assert fromKeys.length == fromValues.length && HashContainers.checkPowerOfTwo(fromKeys.length - 1); // Rehash all stored key/value pairs into the new buffers. final KType[] keys = (KType[]) this.keys; final int[] values = this.values; final int mask = this.mask; KType 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]) == null)) { int slot = hashKey(existing) & mask; while (!((keys[slot]) == null)) { 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. KType[] prevKeys = (KType[]) this.keys; int[] prevValues = this.values; try { int emptyElementSlot = 1; this.keys = ((KType[]) new Object[arraySize + emptyElementSlot]); this.values = (new int[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, KType pendingKey, int pendingValue) {
assert assigned == resizeAt && (((KType) keys[slot]) == null) && !((pendingKey) == null);
// Try to allocate new buffers first. If we OOM, we leave in a consistent state.
final KType[] prevKeys = (KType[]) this.keys;
final int[] 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 KType[] keys = (KType[]) this.keys;
final int[] 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 KType existing = keys[slot];
if (((existing) == null)) {
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] = null;
values[gapSlot] = 0;
assigned--;
}
protected boolean equals(Object v1, Object v2) {
return (v1 == v2) || (v1 != null && v1.equals(v2));
}
}