jodd.util.collection.ClassMap Maven / Gradle / Ivy
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
package jodd.util.collection;
import java.util.Arrays;
/**
* Storage for holding classes keys and values.
* It is NOT a Map
instance. It is very fast
* on un-synchronized lookups, faster then HashMap
.
* Uses identity for checking if Class
keys are equal.
*
* The initial version of this class was provided by @zqq90, from
* WebIt-script
* project. Thank you!
*/
public final class ClassMap {
private static final int MAXIMUM_CAPACITY = 1 << 29;
private Entry[] table;
private int threshold;
private int size;
/**
* Creates new map with given initial capacity.
*/
@SuppressWarnings("unchecked")
public ClassMap(int initialCapacity) {
int initlen;
if (initialCapacity > MAXIMUM_CAPACITY) {
initlen = MAXIMUM_CAPACITY;
}
else {
initlen = 16;
while (initlen < initialCapacity) {
initlen <<= 1;
}
}
this.table = new Entry[initlen];
this.threshold = (int) (initlen * 0.75f);
}
public ClassMap() {
this(64);
}
/**
* Returns total number of stored classes.
*/
public int size() {
return size;
}
/**
* Returns a value associated to a key in unsafe, but very fast way.
*/
public V unsafeGet(final Class key) {
final Entry[] tab = table;
Entry e = (tab)[key.hashCode() & (tab.length - 1)];
while (e != null) {
if (key == e.key) {
return e.value;
}
e = e.next;
}
return null;
}
/**
* Returns a value associated to a key in thread-safe way.
*/
public synchronized V get(Class key) {
return unsafeGet(key);
}
@SuppressWarnings("unchecked")
private void resize() {
if (size < threshold) {
return;
}
final Entry[] oldTable = table;
final int oldCapacity = oldTable.length;
final int newCapacity = oldCapacity << 1;
if (newCapacity > MAXIMUM_CAPACITY) {
if (threshold == MAXIMUM_CAPACITY - 1) {
throw new IllegalStateException("Capacity exhausted");
}
threshold = MAXIMUM_CAPACITY - 1;
return;
}
final int newMark = newCapacity - 1;
final Entry[] newTable = new Entry[newCapacity];
for (int i = oldCapacity; i-- > 0; ) {
int index;
for (Entry old = oldTable[i], e; old != null; ) {
e = old;
old = old.next;
index = e.id & newMark;
e.next = newTable[index];
newTable[index] = e;
}
}
this.threshold = (int) (newCapacity * 0.75f);
// must be last
this.table = newTable;
}
/**
* Associates the specified value with the specified Class in this map.
* Returns the previous value associated with key, or null
* if there was no mapping for key.
*/
@SuppressWarnings("unchecked")
public synchronized V put(Class key, V value) {
final int id;
int index;
Entry[] tab = table;
id = key.hashCode();
index = (id) & (tab.length - 1);
Entry e = (tab)[index];
while (e != null) {
if (key == e.key) { // identity check
// key found, replace
V existing = e.value;
e.value = value;
return existing;
}
e = e.next;
}
if (size >= threshold) {
resize();
tab = table;
index = id & (tab.length - 1);
}
// creates the new entry
tab[index] = new Entry(id, key, value, tab[index]);
size++;
return null;
}
/**
* Clears the class map.
*/
public synchronized void clear() {
size = 0;
Arrays.fill(table, null);
}
/**
* Maps entry.
*/
private static final class Entry {
final int id;
final Class key;
V value;
Entry next;
private Entry(int id, Class key, V value, Entry next) {
this.value = value;
this.id = id;
this.key = key;
this.next = next;
}
}
}