/*
* Copyright (c) 2010, Michael Bien
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of JogAmp nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 Michael Bien 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.
*/
/**
* Created on Sunday, March 28 2010 21:01
*/
package com.jogamp.common.util;
import java.util.Arrays;
import java.util.Iterator;
/*
* Note: this map is used as template for other maps.
*/
/**
* Fast HashMap for primitive data. Optimized for being GC friendly.
* Original code is based on the skorpios project
* released under new BSD license.
*
* @author Michael Bien
* @author Simon Goller
*
* @see IntObjectHashMap
* @see IntLongHashMap
* @see LongObjectHashMap
* @see LongLongHashMap
* @see LongIntHashMap
*/
public class /*name*/IntIntHashMap/*name*/ implements Iterable {
private final float loadFactor;
private Entry[] table;
private int size;
private int mask;
private int capacity;
private int threshold;
private /*value*/int/*value*/ keyNotFoundValue = /*null*/-1/*null*/;
public /*name*/IntIntHashMap/*name*/() {
this(16, 0.75f);
}
public /*name*/IntIntHashMap/*name*/(int initialCapacity) {
this(initialCapacity, 0.75f);
}
public /*name*/IntIntHashMap/*name*/(int initialCapacity, float loadFactor) {
if (initialCapacity > 1 << 30) {
throw new IllegalArgumentException("initialCapacity is too large.");
}
if (initialCapacity < 0) {
throw new IllegalArgumentException("initialCapacity must be greater than zero.");
}
if (loadFactor <= 0) {
throw new IllegalArgumentException("loadFactor must be greater than zero.");
}
capacity = 1;
while (capacity < initialCapacity) {
capacity <<= 1;
}
this.loadFactor = loadFactor;
this.threshold = (int) (capacity * loadFactor);
this.table = new Entry[capacity];
this.mask = capacity - 1;
}
public boolean containsValue(/*value*/int/*value*/ value) {
Entry[] t = this.table;
for (int i = t.length; i-- > 0;) {
for (Entry e = t[i]; e != null; e = e.next) {
if (e.value == value) {
return true;
}
}
}
return false;
}
// @SuppressWarnings(value="cast")
public boolean containsKey(/*key*/int/*key*/ key) {
Entry[] t = this.table;
int index = (int) (key & mask);
for (Entry e = t[index]; e != null; e = e.next) {
if (e.key == key) {
return true;
}
}
return false;
}
/**
* Returns the value to which the specified key is mapped,
* or {@link #getKeyNotFoundValue} if this map contains no mapping for the key.
*/
// @SuppressWarnings(value="cast")
public /*value*/int/*value*/ get(/*key*/int/*key*/ key) {
Entry[] t = this.table;
int index = (int) (key & mask);
for (Entry e = t[index]; e != null; e = e.next) {
if (e.key == key) {
return e.value;
}
}
return keyNotFoundValue;
}
/**
* Maps the key to the specified value. If a mapping to this key already exists,
* the previous value will be returned (otherwise {@link #getKeyNotFoundValue}).
*/
// @SuppressWarnings(value="cast")
public /*value*/int/*value*/ put(/*key*/int/*key*/ key, /*value*/int/*value*/ value) {
Entry[] t = this.table;
int index = (int) (key & mask);
// Check if key already exists.
for (Entry e = t[index]; e != null; e = e.next) {
if (e.key != key) {
continue;
}
/*value*/int/*value*/ oldValue = e.value;
e.value = value;
return oldValue;
}
t[index] = new Entry(key, value, t[index]);
if (size++ >= threshold) {
// Rehash.
int newCapacity = 2 * capacity;
Entry[] newTable = new Entry[newCapacity];
/*key*/int/*key*/ bucketmask = newCapacity - 1;
for (int j = 0; j < t.length; j++) {
Entry e = t[j];
if (e != null) {
t[j] = null;
do {
Entry next = e.next;
index = (int) (e.key & bucketmask);
e.next = newTable[index];
newTable[index] = e;
e = next;
} while (e != null);
}
}
table = newTable;
capacity = newCapacity;
threshold = (int) (newCapacity * loadFactor);
mask = capacity - 1;
}
return keyNotFoundValue;
}
/**
* Copies all of the mappings from the specified map to this map.
*/
// @SuppressWarnings(value="cast")
public void putAll(/*name*/IntIntHashMap/*name*/ source) {
Iterator itr = source.iterator();
while(itr.hasNext()) {
Entry e = (Entry) itr.next();
put(e.key, e.value);
}
}
/**
* Removes the key-value mapping from this map.
* Returns the previously mapped value or {@link #getKeyNotFoundValue} if no such mapping exists.
*/
// @SuppressWarnings(value="cast")
public /*value*/int/*value*/ remove(/*key*/int/*key*/ key) {
Entry[] t = this.table;
int index = (int) (key & mask);
Entry prev = t[index];
Entry e = prev;
while (e != null) {
Entry next = e.next;
if (e.key == key) {
size--;
if (prev == e) {
t[index] = next;
} else {
prev.next = next;
}
return e.value;
}
prev = e;
e = next;
}
return keyNotFoundValue;
}
/**
* Returns the current number of key-value mappings in this map.
*/
public int size() {
return size;
}
/**
* Clears the entire map. The size is 0 after this operation.
*/
public void clear() {
Arrays.fill(table, null);
size = 0;
}
/**
* Returns a new {@link Iterator}.
* Note: this Iterator does not yet support removal of elements.
*/
// @Override
public Iterator/**/ iterator() {
return new EntryIterator(table);
}
/**
* Sets the new key not found value.
* For primitive types (int, long) the default is -1,
* for Object types, the default is null.
*
* @return the previous key not found value
* @see #get
* @see #put
*/
public /*value*/int/*value*/ setKeyNotFoundValue(/*value*/int/*value*/ newKeyNotFoundValue) {
/*value*/int/*value*/ t = keyNotFoundValue;
keyNotFoundValue = newKeyNotFoundValue;
return t;
}
/**
* Returns the value which is returned if no value has been found for the specified key.
* @see #get
* @see #put
*/
public /*value*/int/*value*/ getKeyNotFoundValue() {
return keyNotFoundValue;
}
// @Override
public String toString() {
// TODO use StringBuilder as soon we are at language level 5
String str = "{";
Iterator itr = iterator();
while(itr.hasNext()) {
str += itr.next();
if(itr.hasNext()) {
str += ", ";
}
}
str += "}";
return str;
}
private final static class EntryIterator implements Iterator/**/ {
private final Entry[] entries;
private int index;
private Entry next;
private EntryIterator(Entry[] entries){
this.entries = entries;
// load next
next();
}
// @Override
public boolean hasNext() {
return next != null;
}
// @Override
public Object next() {
Entry current = next;
if(current != null && current.next != null) {
next = current.next;
}else{
while(index < entries.length) {
Entry e = entries[index++];
if(e != null) {
next = e;
return current;
}
}
next = null;
}
return current;
}
// @Override
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
/**
* An entry mapping a key to a value.
*/
public final static class Entry {
public final /*key*/int/*key*/ key;
public /*value*/int/*value*/ value;
private Entry next;
private Entry(/*key*/int/*key*/ k, /*value*/int/*value*/ v, Entry n) {
key = k;
value = v;
next = n;
}
/**
* Returns the key of this entry.
*/
public /*key*/int/*key*/ getKey() {
return key;
}
/**
* Returns the value of this entry.
*/
public /*value*/int/*value*/ getValue() {
return value;
}
/**
* Sets the value for this entry.
*/
public void setValue(/*value*/int/*value*/ value) {
this.value = value;
}
// @Override
public String toString() {
return "["+key+":"+value+"]";
}
}
}