Fix CoordMap [test cases to be integrated later].

This commit is contained in:
asofold 2012-11-10 17:31:17 +01:00
parent 915987f9d3
commit 7fff252863

View File

@ -1,12 +1,17 @@
package fr.neatmonster.nocheatplus.utilities.ds; package fr.neatmonster.nocheatplus.utilities.ds;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
/** /**
* Map int coordinates to values (just for fun). Intended for Minecraft coordinates, probably not for too high values.<br> * Map int coordinates to values (just for fun). Intended for Minecraft
* This implementation is not thread safe, though changing values and get/contains should work if the map stays unchanged. * coordinates, probably not for too high values.<br>
* This implementation is not thread safe, though changing values and
* get/contains should work if the map stays unchanged.
*
* @author mc_dev * @author mc_dev
* *
* @param <V> * @param <V>
@ -21,12 +26,13 @@ public class CoordMap<V> {
return p1 * x ^ p2 * y ^ p3 * z; return p1 * x ^ p2 * y ^ p3 * z;
} }
private static final class Entry<V>{ public static final class Entry<V> {
protected final int x; protected final int x;
protected final int y; protected final int y;
protected final int z; protected final int z;
protected V value; protected V value;
protected final int hash; protected final int hash;
public Entry(final int x, final int y, final int z, final V value, final int hash) { public Entry(final int x, final int y, final int z, final V value, final int hash) {
this.x = x; this.x = x;
this.y = y; this.y = y;
@ -34,6 +40,116 @@ public class CoordMap<V> {
this.value = value; this.value = value;
this.hash = hash; this.hash = hash;
} }
public final int getX(){
return x;
}
public final int getY(){
return y;
}
public final int getZ(){
return z;
}
public final V getValue(){
return value;
}
}
/**
* Does NOT throw anything, not the least thread safe.
*
* @author mc_dev
*
* @param <V>
*/
public static final class CoordMapIterator<V> implements Iterator<Entry<V>> {
private final CoordMap<V> map;
private final List<Entry<V>>[] entries;
/** Current search position. */
private int slot = 0;
/** Current search position. */
private int index = 0;
/** Last found position. */
private int slotLast = -1;
/** Last found position. */
private int indexLast = -1;
protected CoordMapIterator(final CoordMap<V> map) {
this.map = map;
entries = map.entries;
}
@Override
public final boolean hasNext() {
// Also set index and slot to next found element.
while (slot < entries.length) {
final List<Entry<V>> bucket = entries[slot];
if (bucket == null){
slot ++;
index = 0;
}
else {
if (index < bucket.size()) return true;
else {
// Not found, reset.
slot ++;
index = 0;
}
}
}
return false;
}
@Override
public final Entry<V> next() {
while (slot < entries.length) {
final List<Entry<V>> bucket = entries[slot];
if (bucket == null){
slot ++;
index = 0;
}
else {
final int size = bucket.size();
if (index < size) {
final Entry<V> res = bucket.get(index);
slotLast = slot;
indexLast = index;
index++;
if (index == size) {
index = 0;
slot++;
}
return res;
}
else{
// TODO: inconsistent, could be empty though.
slot++;
index = 0;
}
}
}
throw new NoSuchElementException();
}
@Override
public final void remove() {
if (slotLast == -1){
// Next had not been called at all,
// or remove has been called several times.
return;
}
final List<Entry<V>> bucket = entries[slotLast];
bucket.remove(indexLast);
if (bucket.isEmpty()) entries[slotLast] = null;
else if (slotLast == slot) index --;
map.size--;
slotLast = indexLast = -1;
}
} }
// Core data. // Core data.
@ -52,7 +168,8 @@ public class CoordMap<V> {
/** /**
* *
* @param initialCapacity Initial internal array size. <br> * @param initialCapacity
* Initial internal array size. <br>
* TODO: change to expected number of elements (len = cap/load). * TODO: change to expected number of elements (len = cap/load).
* @param loadFactor * @param loadFactor
*/ */
@ -65,6 +182,7 @@ public class CoordMap<V> {
/** /**
* Check if the map contains a value for the given coordinates.<br> * Check if the map contains a value for the given coordinates.<br>
* NOTE: Delegates to get, use get for fastest checks. * NOTE: Delegates to get, use get for fastest checks.
*
* @param x * @param x
* @param y * @param y
* @param z * @param z
@ -76,6 +194,7 @@ public class CoordMap<V> {
/** /**
* Get the value if there is a mapping for the given coordinates. * Get the value if there is a mapping for the given coordinates.
*
* @param x * @param x
* @param y * @param y
* @param z * @param z
@ -85,7 +204,8 @@ public class CoordMap<V> {
final int hash = getHash(x, y, z); final int hash = getHash(x, y, z);
final int slot = Math.abs(hash) % entries.length; final int slot = Math.abs(hash) % entries.length;
final List<Entry<V>> bucket = entries[slot]; final List<Entry<V>> bucket = entries[slot];
if (bucket == null) return null;; if (bucket == null) return null;
;
for (final Entry<V> entry : bucket) { for (final Entry<V> entry : bucket) {
if (hash == entry.hash && x == entry.x && z == entry.z && y == entry.y) return entry.value; if (hash == entry.hash && x == entry.x && z == entry.z && y == entry.y) return entry.value;
} }
@ -94,23 +214,24 @@ public class CoordMap<V> {
/** /**
* Add value with the coordinates + hash from the last contains call. * Add value with the coordinates + hash from the last contains call.
*
* @param value * @param value
* @return If a value was replaced. * @return If a value was replaced.
*/ */
public final boolean put(final int x, final int y, final int z, final V value){ public final boolean put(final int x, final int y, final int z, final V value)
{
final int hash = getHash(x, y, z); final int hash = getHash(x, y, z);
final int absHash = Math.abs(hash); final int absHash = Math.abs(hash);
int slot = absHash % entries.length; int slot = absHash % entries.length;
List<Entry<V>> bucket = entries[slot]; List<Entry<V>> bucket = entries[slot];
if (bucket != null) { if (bucket != null) {
for (final Entry<V> entry : bucket) { for (final Entry<V> entry : bucket) {
if (hash == entry.hash && x == entry.x && y == entry.z && z == entry.y){ if (hash == entry.hash && x == entry.x && z == entry.z && y == entry.y) {
entry.value = value; entry.value = value;
return true; return true;
} }
} }
} } else if (size + 1 > entries.length * loadFactor) {
else if (size + 1 > entries.length * loadFactor){
resize(size + 1); resize(size + 1);
slot = absHash % entries.length; slot = absHash % entries.length;
bucket = entries[slot]; bucket = entries[slot];
@ -120,32 +241,65 @@ public class CoordMap<V> {
bucket = new LinkedList<Entry<V>>(); bucket = new LinkedList<Entry<V>>();
entries[slot] = bucket; entries[slot] = bucket;
} }
entries[slot] = bucket;
bucket.add(new Entry<V>(x, y, z, value, hash)); bucket.add(new Entry<V>(x, y, z, value, hash));
size++; size++;
return false; return false;
} }
/**
* Remove an entry.
*
* @param x
* @param y
* @param z
* @return
*/
public final V remove(final int x, final int y, final int z) {
final int hash = getHash(x, y, z);
final int absHash = Math.abs(hash);
int slot = absHash % entries.length;
final List<Entry<V>> bucket = entries[slot];
if (bucket == null) return null;
else {
for (int i = 0; i < bucket.size(); i++) {
final Entry<V> entry = bucket.get(i);
if (entry.hash == hash && x == entry.x && z == entry.z && y == entry.y) {
bucket.remove(entry);
if (bucket.isEmpty()) entries[slot] = null;
size--;
return entry.value;
}
}
return null;
}
}
private final void resize(final int size) { private final void resize(final int size) {
// TODO: other capacity? // TODO: other capacity / allow to set strategy [also for reducing for long time use]
final int newCapacity = (int) ((size + 4) / loadFactor); final int newCapacity = Math.max((int) ((size + 4) / loadFactor), entries.length + entries.length / 4);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final List<Entry<V>>[] newEntries = new List[newCapacity]; final List<Entry<V>>[] newEntries = new List[newCapacity];
int used = -1; // Fill old buckets to fornt of old array.
for (int oldSlot = 0; oldSlot < entries.length; oldSlot++) { for (int oldSlot = 0; oldSlot < entries.length; oldSlot++) {
final List<Entry<V>> oldBucket = entries[oldSlot]; final List<Entry<V>> oldBucket = entries[oldSlot];
if (oldBucket != null){ if (oldBucket == null) continue;
for (final Entry<V> entry : oldBucket) { for (final Entry<V> entry : oldBucket) {
final int newSlot = Math.abs(entry.hash) % newCapacity; final int newSlot = Math.abs(entry.hash) % newCapacity;
List<Entry<V>> newBucket = newEntries[oldSlot]; List<Entry<V>> newBucket = newEntries[newSlot];
if (newBucket == null) { if (newBucket == null) {
newBucket = new LinkedList<Entry<V>>(); if (used < 0) newBucket = new LinkedList<Entry<V>>();
else{
newBucket = entries[used];
entries[used] = null;
used--;
}
newEntries[newSlot] = newBucket; newEntries[newSlot] = newBucket;
} }
newBucket.add(entry); newBucket.add(entry);
} }
oldBucket.clear(); oldBucket.clear();
}
entries[oldSlot] = null; entries[oldSlot] = null;
entries[++used] = oldBucket;
} }
entries = newEntries; entries = newEntries;
} }
@ -159,4 +313,8 @@ public class CoordMap<V> {
Arrays.fill(entries, null); Arrays.fill(entries, null);
// TODO: resize ? // TODO: resize ?
} }
public final Iterator<Entry<V>> iterator(){
return new CoordMapIterator<V>(this);
}
} }