/*
* ArrayMap.java Created Aug 12, 2009 by Andrew Butler, PSL
*/
//package prisms.util;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* An ArrayMap is a very inefficient map type that is more robust in dealing
* with changes to its keys than other maps. HashMaps and TreeMaps may "lose"
* the reference to a value if the key to that value changes in a way that
* causes it to map or compare differently. ArrayMap has no such limitations,
* but it pays for this feature in its speed with large data sets. Searching and
* insertion are both O(n). Another bonus feature is that ArrayMap relies only
* on the equals method--it requires no contract with the hashCode() and no
* comparators.
*
* @param
* The key type for this map
* @param
* The value type for this map
*/
public class ArrayMap implements Map {
private Object[] theKeys;
private Object[] theValues;
/**
* Creates an ArrayMap
*/
public ArrayMap() {
theKeys = new Object[0];
theValues = new Object[0];
}
/**
* @see java.util.Map#size()
*/
public int size() {
return theKeys.length;
}
/**
* @see java.util.Map#isEmpty()
*/
public boolean isEmpty() {
return theKeys.length == 0;
}
/**
* @see java.util.Map#get(java.lang.Object)
*/
public V get(Object key) {
for (int i = 0; i < theKeys.length; i++)
if (equal(key, theKeys[i]))
return (V) theValues[i];
return null;
}
private static boolean equal(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
/**
* @see java.util.Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key) {
for (int i = 0; i < theKeys.length; i++)
if (equal(key, theKeys[i]))
return true;
return false;
}
/**
* @see java.util.Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value) {
for (int i = 0; i < theValues.length; i++)
if (equal(value, theValues[i]))
return true;
return false;
}
/**
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
*/
public V put(K key, V value) {
for (int i = 0; i < theKeys.length; i++) {
if (equal(key, theKeys[i])) {
V old = (V) theValues[i];
theKeys[i] = key;
theValues[i] = value;
return old;
}
}
Object[] newKeys = add(theKeys, key);
Object[] newVals = add(theValues, value);
theKeys = newKeys;
theValues = newVals;
return null;
}
/**
* @param
* The type of the array
* @param anArray
* The array to extend
* @param anElement
* The element to add into anArray
* @return A new array of length anArray.length+1
whose
* contents are those of anArray
followed by
* anElement
*/
public static T[] add(T[] anArray, T anElement) {
int len = anArray == null ? 0 : Array.getLength(anArray);
return add(anArray, anElement, len);
}
/**
* Inserts an element into the array
*
* @param
* The type of the object array
* @param anArray
* The array to insert into
* @param anElement
* The element to insert
* @param anIndex
* The index for the new element
* @return The new array with all elements of anArray
, but with
* anElement
inserted at index anIndex
*/
public static T[] add(T[] anArray, T anElement, int anIndex) {
T[] ret;
if (anArray == null) {
if (anIndex != 0)
throw new ArrayIndexOutOfBoundsException("Cannot set "
+ anIndex + " element in a null array");
ret = (T[]) Array.newInstance(anElement.getClass(), 1);
ret[0] = anElement;
return ret;
} else
ret = (T[]) Array.newInstance(
anArray.getClass().getComponentType(), anArray.length + 1);
System.arraycopy(anArray, 0, ret, 0, anIndex);
put(ret, anElement, anIndex);
System.arraycopy(anArray, anIndex, ret, anIndex + 1, anArray.length
- anIndex);
return ret;
}
private static void put(Object array, Object element, int index) {
try {
if (array instanceof Object[]) {
try {
((Object[]) array)[index] = element;
} catch (ArrayStoreException e) {
throw new IllegalArgumentException(e.getMessage()
+ ": "
+ (element == null ? "null" : element.getClass()
.getName()) + " into "
+ array.getClass().getName());
}
} else {
try {
Array.set(array, index, element);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(e.getMessage()
+ ": "
+ (element == null ? "null" : element.getClass()
.getName()) + " into "
+ array.getClass().getName(), e);
}
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new ArrayIndexOutOfBoundsException(index + " into "
+ Array.getLength(array));
}
}
/**
* @see java.util.Map#putAll(java.util.Map)
*/
public void putAll(Map extends K, ? extends V> t) {
for (Map.Entry extends K, ? extends V> entry : t.entrySet())
put(entry.getKey(), entry.getValue());
}
/**
* @see java.util.Map#remove(java.lang.Object)
*/
public V remove(Object key) {
for (int i = 0; i < theKeys.length; i++) {
if (equal(theKeys[i], key)) {
V old = (V) theValues[i];
Object[] newKeys = remove(theKeys, i);
Object[] newVals = remove(theValues, i);
theKeys = newKeys;
theValues = newVals;
return old;
}
}
return null;
}
/**
* Removes the specified object from the array
*
* @param
* The type of the array
* @param anArray
* The array to remove an element from
* @param anIndex
* The index of the element to remove
* @return A new array with all the elements of anArray
except
* the element at anIndex
*/
public static T[] remove(T[] anArray, int anIndex) {
T[] ret;
if (anArray == null)
return null;
else {
ret = (T[]) Array.newInstance(
anArray.getClass().getComponentType(), anArray.length - 1);
}
System.arraycopy(anArray, 0, ret, 0, anIndex);
System.arraycopy(anArray, anIndex + 1, ret, anIndex, anArray.length
- anIndex - 1);
return ret;
}
/**
* @see java.util.Map#clear()
*/
public void clear() {
theKeys = new Object[0];
theValues = new Object[0];
}
/**
* @see java.util.Map#keySet()
*/
public Set keySet() {
final Object[] iterKeys = theKeys;
return new java.util.AbstractSet() {
@Override
public int size() {
return iterKeys.length;
}
@Override
public Iterator iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() {
return index < iterKeys.length;
}
public K next() {
K ret = (K) iterKeys[index];
index++;
return ret;
}
public void remove() {
ArrayMap.this.remove(iterKeys[index - 1]);
}
};
}
};
}
/**
* @see java.util.Map#values()
*/
public Collection values() {
final Object[] iterKeys = theKeys;
final Object[] iterVals = theValues;
return new java.util.AbstractSet() {
@Override
public int size() {
return iterVals.length;
}
@Override
public Iterator iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() {
return index < iterVals.length;
}
public V next() {
V ret = (V) iterVals[index];
index++;
return ret;
}
public void remove() {
ArrayMap.this.remove(iterKeys[index - 1]);
}
};
}
};
}
/**
* @see java.util.Map#entrySet()
*/
public Set> entrySet() {
final Object[] iterKeys = theKeys;
final Object[] iterVals = theValues;
return new java.util.AbstractSet>() {
@Override
public int size() {
return iterVals.length;
}
@Override
public Iterator> iterator() {
return new Iterator>() {
private int index = 0;
public boolean hasNext() {
return index < iterVals.length;
}
public Map.Entry next() {
final K entryKey = (K) iterKeys[index];
final V[] entryVal = (V[]) new Object[] { iterVals[index] };
Map.Entry ret = new Map.Entry() {
public K getKey() {
return entryKey;
}
public V getValue() {
return entryVal[0];
}
public V setValue(V value) {
ArrayMap.this.put(entryKey, value);
V ret2 = entryVal[0];
entryVal[0] = value;
return ret2;
}
};
index++;
return ret;
}
public void remove() {
ArrayMap.this.remove(iterKeys[index - 1]);
}
};
}
};
}
}