io.hyperfoil.api.collection.Lookup Maven / Gradle / Ivy
package io.hyperfoil.api.collection;
import java.lang.reflect.Array;
import java.util.function.Supplier;
/**
* Primitive hashmap with limited insertion capability. Provides cached arrays with all values.
*/
public class Lookup {
final Class clazz;
final Supplier factory;
Object[] keys;
V[] values;
V[] array;
int size;
public Lookup(Class clazz, Supplier factory) {
this.clazz = clazz;
this.factory = factory;
this.keys = new Object[4];
this.values = newArray(4);
}
public V get(K key) {
int mask = keys.length - 1;
int slot = key.hashCode() & mask;
for (; ; ) {
Object k2 = keys[slot];
if (k2 == null) {
return null;
} else if (k2.equals(key)) {
return values[slot];
}
slot = (slot + 1) & mask;
}
}
public V reserve(K key) {
V existing = get(key);
if (existing != null) {
return existing;
}
// we aim at 50% occupancy at most
if (++size * 2 > values.length) {
int newSize = keys.length * 2;
Object[] newKeys = new Object[newSize];
V[] newValues = newArray(newSize);
int mask = newSize - 1;
for (int i = 0; i < keys.length; ++i) {
Object k = keys[i];
if (k != null) {
insert(newKeys, newValues, mask, k, values[i]);
}
}
keys = newKeys;
values = newValues;
}
V newValue = factory.get();
insert(keys, values, keys.length - 1, key, newValue);
array = null;
return newValue;
}
private void insert(Object[] newKeys, V[] newValues, int mask, Object k, V v) {
int slot = k.hashCode() & mask;
Object otherKey;
while ((otherKey = newKeys[slot]) != null) {
assert !otherKey.equals(k);
slot = (slot + 1) & mask;
}
newKeys[slot] = k;
newValues[slot] = v;
}
@SuppressWarnings("unchecked")
private V[] newArray(int size) {
return (V[]) Array.newInstance(clazz, size);
}
/**
* @return Cached map with the values. Should not be modified!
*/
public V[] array() {
if (array == null) {
array = newArray(size);
int i = 0;
for (V v : values) {
if (v != null) {
array[i++] = v;
}
}
}
return array;
}
}