org.jboss.marshalling.util.IntKeyMap Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 org.jboss.marshalling.util;
import java.io.IOException;
import java.io.Serializable;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* An integer-keyed map, optimized for fast copying. Based on {@code FastCopyHashMap} by Jason T. Greene.
*
* @author Jason T. Greene
* @author David M. Lloyd
*/
public final class IntKeyMap implements Cloneable, Serializable, Iterable> {
/**
* Same default as HashMap, must be a power of 2
*/
private static final int DEFAULT_CAPACITY = 8;
/**
* MAX_INT - 1
*/
private static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 67%, just like IdentityHashMap
*/
private static final float DEFAULT_LOAD_FACTOR = 0.67f;
/**
* The open-addressed table
*/
private transient Entry[] table;
/**
* The current number of key-value pairs
*/
private transient int size;
/**
* The next resize
*/
private transient int threshold;
/**
* The user defined load factor which defines when to resize
*/
private final float loadFactor;
private static final long serialVersionUID = -6864280848239317243L;
public IntKeyMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Can not have a negative size table!");
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (!(loadFactor > 0F && loadFactor <= 1F))
throw new IllegalArgumentException("Load factor must be greater than 0 and less than or equal to 1");
this.loadFactor = loadFactor;
init(initialCapacity, loadFactor);
}
@SuppressWarnings("unchecked")
public IntKeyMap(IntKeyMap extends V> map) {
table = (Entry[]) map.table.clone();
loadFactor = map.loadFactor;
size = map.size;
threshold = map.threshold;
}
@SuppressWarnings("unchecked")
private void init(int initialCapacity, float loadFactor) {
int c = 1;
for (; c < initialCapacity; c <<= 1) ;
threshold = (int) (c * loadFactor);
// Include the load factor when sizing the table for the first time
if (initialCapacity > threshold && c < MAXIMUM_CAPACITY) {
c <<= 1;
threshold = (int) (c * loadFactor);
}
table = (Entry[]) new Entry[c];
}
public IntKeyMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public IntKeyMap() {
this(DEFAULT_CAPACITY);
}
private int nextIndex(int index, int length) {
index = (index >= length - 1) ? 0 : index + 1;
return index;
}
private static boolean eq(Object o1, Object o2) {
return o1 == o2 || (o1 != null && o1.equals(o2));
}
private static int index(int hashCode, int length) {
return hashCode & (length - 1);
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public V get(int key) {
int length = table.length;
int index = index(key, length);
for (int start = index; ;) {
Entry e = table[index];
if (e == null)
return null;
if (e.key == key)
return e.value;
index = nextIndex(index, length);
if (index == start) // Full table
return null;
}
}
public boolean containsKey(int key) {
int length = table.length;
int index = index(key, length);
for (int start = index; ;) {
Entry e = table[index];
if (e == null)
return false;
if (e.key == key)
return true;
index = nextIndex(index, length);
if (index == start) // Full table
return false;
}
}
public boolean containsValue(Object value) {
for (Entry e : table)
if (e != null && eq(value, e.value))
return true;
return false;
}
public V put(int key, V value) {
Entry[] table = this.table;
int hash = key;
int length = table.length;
int index = index(hash, length);
for (int start = index; ;) {
Entry e = table[index];
if (e == null)
break;
if (e.key == key) {
table[index] = new Entry(key, value);
return e.value;
}
index = nextIndex(index, length);
if (index == start)
throw new IllegalStateException("Table is full!");
}
table[index] = new Entry(key, value);
if (++size >= threshold)
resize(length);
return null;
}
@SuppressWarnings("unchecked")
private void resize(int from) {
int newLength = from << 1;
// Can't get any bigger
if (newLength > MAXIMUM_CAPACITY || newLength <= from)
return;
Entry[] newTable = new Entry[newLength];
Entry[] old = table;
for (Entry e : old) {
if (e == null)
continue;
int index = index(e.key, newLength);
while (newTable[index] != null)
index = nextIndex(index, newLength);
newTable[index] = e;
}
threshold = (int) (loadFactor * newLength);
table = newTable;
}
public V remove(int key) {
Entry[] table = this.table;
int length = table.length;
int start = index(key, length);
for (int index = start; ;) {
Entry e = table[index];
if (e == null)
return null;
if (e.key == key) {
table[index] = null;
relocate(index);
size--;
return e.value;
}
index = nextIndex(index, length);
if (index == start)
return null;
}
}
private void relocate(int start) {
Entry[] table = this.table;
int length = table.length;
int current = nextIndex(start, length);
for (; ;) {
Entry e = table[current];
if (e == null)
return;
// A Doug Lea variant of Knuth's Section 6.4 Algorithm R.
// This provides a non-recursive method of relocating
// entries to their optimal positions once a gap is created.
int prefer = index(e.key, length);
if ((current < prefer && (prefer <= start || start <= current))
|| (prefer <= start && start <= current)) {
table[start] = e;
table[current] = null;
start = current;
}
current = nextIndex(current, length);
}
}
public void clear() {
Entry[] table = this.table;
for (int i = 0; i < table.length; i++)
table[i] = null;
size = 0;
}
@SuppressWarnings("unchecked")
public IntKeyMap clone() {
try {
IntKeyMap clone = (IntKeyMap) super.clone();
clone.table = table.clone();
return clone;
}
catch (CloneNotSupportedException e) {
// should never happen
throw new IllegalStateException(e);
}
}
public void printDebugStats() {
int optimal = 0;
int total = 0;
int totalSkew = 0;
int maxSkew = 0;
for (int i = 0; i < table.length; i++) {
Entry e = table[i];
if (e != null) {
total++;
int target = index(e.key, table.length);
if (i == target)
optimal++;
else {
int skew = Math.abs(i - target);
if (skew > maxSkew) maxSkew = skew;
totalSkew += skew;
}
}
}
System.out.println(" Size: " + size);
System.out.println(" Real Size: " + total);
System.out.println(" Optimal: " + optimal + " (" + (float) optimal * 100 / total + "%)");
System.out.println(" Average Distance: " + ((float) totalSkew / (total - optimal)));
System.out.println(" Max Distance: " + maxSkew);
}
@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
int size = s.readInt();
init(size, loadFactor);
for (int i = 0; i < size; i++) {
int key = s.readInt();
V value = (V) s.readObject();
putForCreate(key, value);
}
this.size = size;
}
@SuppressWarnings("unchecked")
private void putForCreate(int key, V value) {
Entry[] table = this.table;
int length = table.length;
int index = index(key, length);
Entry e = table[index];
while (e != null) {
index = nextIndex(index, length);
e = table[index];
}
table[index] = new Entry(key, value);
}
private void writeObject(java.io.ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeInt(size);
for (Entry e : table) {
if (e != null) {
s.writeInt(e.key);
s.writeObject(e.value);
}
}
}
/**
* Iterate over the entries. Read-only operation.
*
* @return the entry iterator
*/
public Iterator> iterator() {
return new Iterator>() {
int i = 0;
public boolean hasNext() {
final Entry[] table = IntKeyMap.this.table;
final int len = table.length;
if (i == len) {
return false;
}
while (table[i] == null) {
if (++i == len) {
return false;
}
}
return false;
}
public Entry next() {
if (! hasNext()) {
throw new NoSuchElementException();
}
return table[i++];
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* A map entry.
*
* @param the value type
*/
public static final class Entry {
private final int key;
private final V value;
private Entry(int key, V value) {
this.key = key;
this.value = value;
}
public int getKey() {
return key;
}
public V getValue() {
return value;
}
}
}