diff options
author | Simon Goller <[email protected]> | 2010-04-12 22:38:23 +0200 |
---|---|---|
committer | Simon Goller <[email protected]> | 2010-04-12 22:38:23 +0200 |
commit | c55d6d0d640ef583a76c3c92d58fbfdb0fc87510 (patch) | |
tree | f7b1b94e67b157a9725fa259463e4946af525cf6 | |
parent | 4b6abbebf1e67642620a2643376f3db6a035a78c (diff) |
IntIntHashMap changed to LongIntHashMap.
-rw-r--r-- | src/java/com/jogamp/common/util/LongIntHashMap.java | 196 | ||||
-rw-r--r-- | test/junit/com/jogamp/common/util/LongIntHashMapTest.java | 139 |
2 files changed, 335 insertions, 0 deletions
diff --git a/src/java/com/jogamp/common/util/LongIntHashMap.java b/src/java/com/jogamp/common/util/LongIntHashMap.java new file mode 100644 index 0000000..ed8f27b --- /dev/null +++ b/src/java/com/jogamp/common/util/LongIntHashMap.java @@ -0,0 +1,196 @@ +/* + * 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; + +/** + * Fast HashMap for primitive data.. + * Original code is based on the <a href="http://code.google.com/p/skorpios/"> skorpios project</a> + * released under new BSD license. + * @author Michael Bien + * @see IntObjectHashMap + * @see IntLongHashMap + */ +public class /*name*/LongIntHashMap/*name*/ { + + private final float loadFactor; + + private Entry[] table; + + private int size; + private int mask; + private int capacity; + private int threshold; + + public /*name*/LongIntHashMap/*name*/() { + this(16, 0.75f); + } + + public /*name*/LongIntHashMap/*name*/(int initialCapacity) { + this(initialCapacity, 0.75f); + } + + public /*name*/LongIntHashMap/*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("initialCapacity 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[] table = this.table; + for (int i = table.length; i-- > 0;) { + for (Entry e = table[i]; e != null; e = e.next) { + if (e.value == value) { + return true; + } + } + } + return false; + } + + public boolean containsKey(/*key*/long/*key*/ key) { + /*key*/int/*key*/ index = (int) (key & mask); + for (Entry e = table[index]; e != null; e = e.next) { + if (e.key == key) { + return true; + } + } + return false; + } + + public /*value*/int/*value*/ get(/*key*/long/*key*/ key) { + int index = (int) (key & mask); + for (Entry e = table[index]; e != null; e = e.next) { + if (e.key == key) { + return e.value; + } + } + return /*null*/0/*null*/; + } + + public /*value*/int/*value*/ put(/*key*/long/*key*/ key, /*value*/int/*value*/ value) { + int index = (int) (key & mask); + // Check if key already exists. + for (Entry e = table[index]; e != null; e = e.next) { + if (e.key != key) { + continue; + } + /*value*/int/*value*/ oldValue = e.value; + e.value = value; + return oldValue; + } + table[index] = new Entry(key, value, table[index]); + if (size++ >= threshold) { + // Rehash. + int newCapacity = 2 * capacity; + Entry[] newTable = new Entry[newCapacity]; + Entry[] src = table; + long bucketmask = newCapacity - 1; + for (int j = 0; j < src.length; j++) { + Entry e = src[j]; + if (e != null) { + src[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 /*null*/0/*null*/; + } + + public /*value*/int/*value*/ remove(/*key*/long/*key*/ key) { + int index = (int) (key & mask); + Entry prev = table[index]; + Entry e = prev; + while (e != null) { + Entry next = e.next; + if (e.key == key) { + size--; + if (prev == e) { + table[index] = next; + } else { + prev.next = next; + } + return e.value; + } + prev = e; + e = next; + } + return /*null*/0/*null*/; + } + + public int size() { + return size; + } + + public void clear() { + Entry[] table = this.table; + for (int index = table.length; --index >= 0;) { + table[index] = null; + } + size = 0; + } + + private final static class Entry { + + private final /*key*/long/*key*/ key; + private /*value*/int/*value*/ value; + private Entry next; + + private Entry(/*key*/long/*key*/ k, /*value*/int/*value*/ v, Entry n) { + key = k; + value = v; + next = n; + } + } +} diff --git a/test/junit/com/jogamp/common/util/LongIntHashMapTest.java b/test/junit/com/jogamp/common/util/LongIntHashMapTest.java new file mode 100644 index 0000000..b69ba43 --- /dev/null +++ b/test/junit/com/jogamp/common/util/LongIntHashMapTest.java @@ -0,0 +1,139 @@ +/** + * Created on Sunday, March 28 2010 21:01 + */ +package com.jogamp.common.util; + +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.Random; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import static java.lang.System.*; + +/** + * + * @author Michael Bien + */ +public class LongIntHashMapTest { + + private static int iterations; + private static long[] rndKeys; + private static int[] rndValues; + + @BeforeClass + public static void init() { + + iterations = 20000; + final int keySeed = 42; + final int valueSeed = 23; + + Random keyRnd = new Random(/*keySeed*/); + Random valueRnd = new Random(/*valueSeed*/); + + rndKeys = new long[iterations]; + rndValues = new int[iterations]; + for (int i = 0; i < iterations; i++) { + rndValues[i] = valueRnd.nextInt(); + rndKeys[i] = keyRnd.nextLong(); + } + + } + /** + * Test of put method, of class LongIntHashMap. + */ + @Test + public void testPutRemove() { + + final LongIntHashMap intmap = new LongIntHashMap(); + final HashMap<Long, Integer> map = new HashMap<Long, Integer>(); + + // put + for (int i = 0; i < iterations; i++) { + intmap.put(rndKeys[i], rndValues[i]); + + assertTrue(intmap.containsValue(rndValues[i])); + assertTrue(intmap.containsKey(rndKeys[i])); + } + + for (int i = 0; i < iterations; i++) { + map.put(rndKeys[i], rndValues[i]); + } + + assertEquals(map.size(), intmap.size()); + + for (Entry<Long, Integer> entry : map.entrySet()) { + assertTrue(intmap.containsKey(entry.getKey())); + assertTrue(intmap.containsValue(entry.getValue())); + } + + int i = 0; + for (Entry<Long, Integer> entry : map.entrySet()) { + assertEquals((int)entry.getValue(), intmap.remove(entry.getKey())); + assertEquals(map.size() - i - 1, intmap.size()); + i++; + } + + } + + @Test + public void benchmark() { + + // simple benchmark + final LongIntHashMap intmap = new LongIntHashMap(1024); + final HashMap<Long, Integer> map = new HashMap<Long, Integer>(1024); + + out.println("put"); + long time = currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + intmap.put(rndKeys[i], rndValues[i]); + } + long intmapTime = (currentTimeMillis() - time); + out.println(" iimap: " + intmapTime+"ms"); + + + time = currentTimeMillis(); + for (int i = 0; i < iterations; i++) { + map.put(rndKeys[i], rndValues[i]); + } + long mapTime = (currentTimeMillis() - time); + out.println(" map: " + mapTime+"ms"); + + assertTrue(intmapTime <= mapTime); + + + System.out.println(); + System.out.println("get"); + intmapTime = (currentTimeMillis() - time); + out.println(" iimap: " + intmapTime+"ms"); + for (int i = 0; i < iterations; i++) { + intmap.get(rndValues[i]); + } + + mapTime = (currentTimeMillis() - time); + out.println(" map: " + mapTime+"ms"); + for (int i = 0; i < iterations; i++) { + map.get(rndValues[i]); + } + assertTrue(intmapTime <= mapTime); + + + out.println(); + out.println("remove"); + intmapTime = (currentTimeMillis() - time); + out.println(" iimap: " + intmapTime+"ms"); + for (int i = 0; i < iterations; i++) { + intmap.remove(rndValues[i]); + } + + mapTime = (currentTimeMillis() - time); + out.println(" map: " + mapTime+"ms"); + for (int i = 0; i < iterations; i++) { + map.remove(rndValues[i]); + } + + assertTrue(intmapTime <= mapTime); + } + + +} |