com.netflix.zeno.util.collections.heapfriendly.HeapFriendlyHashMap Maven / Gradle / Ivy
/*
*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.netflix.zeno.util.collections.heapfriendly;
import static com.netflix.zeno.util.collections.heapfriendly.HeapFriendlyMapArrayRecycler.INDIVIDUAL_OBJECT_ARRAY_SIZE;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
/**
*
* The HeapFriendlyDerivableKeyHashMap is an open-addressed, linear probing hash table. It uses two segmented arrays, one to hold the keys
* and one to hold the values.
*
* @author dkoszewnik
*
*/
public class HeapFriendlyHashMap extends AbstractHeapFriendlyMap {
private final Object[][] keys;
private final Object[][] values;
private final int numBuckets;
private final int maxSize;
private int size;
public HeapFriendlyHashMap(int numEntries) {
int arraySize = numEntries * 10 / 7; // 70% load factor
arraySize = 1 << (32 - Integer.numberOfLeadingZeros(arraySize)); // next power of 2
arraySize = Math.max(arraySize, INDIVIDUAL_OBJECT_ARRAY_SIZE);
this.numBuckets = arraySize;
this.maxSize = numEntries;
this.keys = createSegmentedObjectArray(arraySize);
this.values = createSegmentedObjectArray(arraySize);
}
private Object[][] createSegmentedObjectArray(int arraySize) {
int numArrays = arraySize / INDIVIDUAL_OBJECT_ARRAY_SIZE;
HeapFriendlyMapArrayRecycler recycler = HeapFriendlyMapArrayRecycler.get();
Object[][] segmentedArray = new Object[numArrays][];
for(int i=0;i= maxSize && !containsKey(key))
throw new UnsupportedOperationException("Cannot add more elements than " + maxSize);
if(key == null || value == null)
throw new NullPointerException("Null keys / values not supported in HeapFriendlyHashMap");
int hashCode = rehash(key.hashCode());
/// numBuckets is a power of 2, so the operation [x & (numBuckets - 1)]
/// is equivalent to [Math.abs(x % numBuckets)]
int bucket = hashCode & (numBuckets - 1);
K foundKey = (K) segmentedGet(keys, bucket);
while(foundKey != null && !foundKey.equals(key)) {
bucket = (bucket + 1) & (numBuckets - 1);
foundKey = (K) segmentedGet(keys, bucket);
}
V foundValue = (V) segmentedGet(values, bucket);
segmentedSet(keys, bucket, key);
segmentedSet(values, bucket, value);
if(foundValue == null)
size++;
return foundValue;
}
@Override
@SuppressWarnings("unchecked")
public V get(Object key) {
if(key == null)
return null;
int hashCode = rehash(key.hashCode());
/// numBuckets is a power of 2, so the operation [x & (numBuckets - 1)]
/// is equivalent to [Math.abs(x % numBuckets)]
int bucket = hashCode & (numBuckets - 1);
K foundKey = (K) segmentedGet(keys, bucket);
while(foundKey != null) {
if(foundKey.equals(key)) {
return (V) segmentedGet(values, bucket);
}
bucket = (bucket + 1) & (numBuckets - 1);
foundKey = (K) segmentedGet(keys, bucket);
}
return null;
}
@Override
public boolean containsKey(Object key) {
if(key == null)
return false;
return get(key) != null;
}
private int rehash(int hash) {
hash = ~hash + (hash << 15);
hash = hash ^ (hash >>> 12);
hash = hash + (hash << 2);
hash = hash ^ (hash >>> 4);
hash = hash * 2057;
hash = hash ^ (hash >>> 16);
return hash;
}
@Override
public int size() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public boolean containsValue(Object value) {
for(V foundValue : values()) {
if(foundValue.equals(value)) {
return true;
}
}
return false;
}
@Override
public Set keySet() {
return new AbstractSet() {
public Iterator iterator() {
return new HeapFriendlyMapIterator(keys, numBuckets);
}
@Override
public boolean contains(Object value) {
return containsKey(value);
}
@Override
public int size() {
return size;
}
};
}
@Override
public Collection values() {
return new AbstractSet() {
@Override
public Iterator iterator() {
return new HeapFriendlyMapIterator(values, numBuckets);
}
@Override
public int size() {
return size;
}
};
}
@Override
public Set> entrySet() {
return new AbstractSet>() {
@Override
public Iterator> iterator() {
return new HeapFriendlyMapIterator>(keys, numBuckets) {
@Override
@SuppressWarnings("unchecked")
public Map.Entry next() {
if(current >= numBuckets)
throw new NoSuchElementException();
K key = (K) segmentedGet(segmentedArray, current);
Entry entry = new Entry(key, (V) segmentedGet(values, current));
moveToNext();
return entry;
}
};
}
@Override
public int size() {
return size;
}
};
}
@Override
public void releaseObjectArrays() {
releaseObjectArrays(keys);
releaseObjectArrays(values);
}
private static class Entry extends AbstractHeapFriendlyMapEntry {
private final K key;
private final V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy