/* * HT.java * * Created by aw on Nov 23, 2005. */ package nuggets.util; import java.io.Serializable; import java.util.Arrays; /** * HT * * @author aw * @since Nov 23, 2005 */ public class IdentityHashMap implements Serializable, Cloneable { private static final Object NULL = null; /** stores key value pairs */ private Object[] objects; private Object[] values; /** stores the load */ private double load; /** masks the adresses */ private int mask; /** maximum of entries before rehash */ private int limit; /** the number of entries so far */ private int size; /** * Constructor for HT. Creates a hashtable with capacity of eight and load * equal 0.5. */ public IdentityHashMap() { this(8, 0.5); } /** * Constructor for HT. Creates a hashtable of given initial capacity and the * specified load factor. If the number of elements is larger than capacity * multiplied with load factor the table will be resized by factor of two. * * @param capacity * The intitial capacity of the table. Must be larger than 0. * @param load * The load factor. (0.0<load≤1.0) */ public IdentityHashMap(int capacity, double load) { // check values if(capacity <= 0) throw new IllegalArgumentException("Initial capacity too low: 0>=" + capacity); if(load <= 0.0) throw new IllegalArgumentException("Load factor out of range: 0>=" + load); if(load > 1.0) throw new IllegalArgumentException("Load factor out of range: 1.0<" + load); init(capacity); this.load = load; } /** * Stores a value under the key. * * @param key * @param value * @return the Object previously stored at this position or <code>null</code> */ public Object put(Object key, Object value) { final int a = hash(key); int i = a; Object tKey; do { tKey = objects[i]; if(tKey == null) { // insert new entry objects[i] = key; values[i] = value; if(++size > limit) rehash(); return NULL; } if(tKey == key) { // replace the entry objects[i] = key; Object tValue = values[i]; values[i] = value; return tValue; } if(--i < 0) i = mask; } while(i != a); // table is overload (e.g. load==0.99) rehash(); reinsert(key, value); return NULL; } /** * Returns the value stored under the key. * * @param key * @return the Object stored under the key or <code>null</code> */ public Object get(Object key) { final int a = hash(key); int i = a; Object tKey; for(i = a; i >= 0; i--) { tKey = objects[i]; if(tKey == null) return NULL; if(tKey == key) return values[i]; } for(i = mask; i > a; i--) { tKey = objects[i]; if(tKey == null) return NULL; if(tKey == key) return values[i]; } return NULL; } /** * @param key * @return true or false */ public boolean containsKey(Object key) { final int a = hash(key); int i = a; Object tKey; for(i = a; i >= 0; i--) { tKey = objects[i]; if(tKey == null) return false; if(tKey == key) return true; } for(i = mask; i > a; i--) { tKey = objects[i]; if(tKey == null) return false; if(tKey == key) return true; } return false; } /** * Removes an entry with the specified key. * * @param key * @return the Object previously stored under the key or <code>null</code> */ public Object remove(Object key) { final int a = hash(key); int i = a; Object tKey; do { tKey = objects[i]; if(tKey == null) return NULL; if(tKey == key) { // delete the entry objects[i] = null; Object tValue = values[i]; values[i] = NULL; size--; rehashFrom(i - 1); return tValue; } if(--i < 0) i = mask; } while(i != a); return NULL; } /** * sets the size to 0 and deletes all entries */ public void clear() { size = 0; Arrays.fill(objects, null); Arrays.fill(values, NULL); } /** * @return the number of elements in this hashtable */ public int getSize() { return size; } /** * @return the capacity of this table */ public int getCapacity() { return objects.length >> 1; } /** * Creates a shallow copy of this hashtable. All the structure of the * hashtable itself is copied, but the keys and values are not cloned. * * @return a clone of the hashtable. */ public Object clone() { try { IdentityHashMap t = (IdentityHashMap)super.clone(); t.objects = (Object[])objects.clone(); return t; } catch(CloneNotSupportedException e) { throw new InternalError(e + " in a clonable hashtable"); } } /** * It will resize this table by factor of two */ protected void rehash() { Object[] tObjects = objects; Object[] tIds = values; // update the values init(objects.length << 1); // reinsert old keys for(int i = tObjects.length; i > 0;) { Object tKey = tObjects[--i]; if(tKey != null) reinsert(tKey, tIds[i]); } } /** Resets the table to specified length * @param capacity */ protected void init(int capacity) { this.objects = new Object[capacity]; this.values = new Object[capacity]; this.mask = capacity - 1; this.limit = (int)(Math.ceil(load * objects.length)); } /** * @param a */ protected void rehashFrom(final int a) { int i = a; Object tKey; Object tValue; // restore lists for(i = a; i >= 0; i--) { tKey = objects[i]; if(tKey == null) return; // remove the binding objects[i] = null; tValue = values[i]; values[i] = NULL; // reinsert following entries reinsert(tKey, tValue); } for(i = mask; i > a; i--) { tKey = objects[i]; if(tKey == null) return; // remove the binding objects[i] = null; tValue = values[i]; values[i] = NULL; // reinsert following entries reinsert(tKey, tValue); } } /** * This is a short-cut version of put. It assumes that the key is not in the * table and it returns no value. There must be one free slot in the table. * * @param key * @param value */ protected final void reinsert(Object key, Object value) { final int a = hash(key); for(int i = a; i >= 0; i--) if(objects[i] == null) { objects[i] = key; values[i] = value; return; } for(int i = mask; i > a; i--) if(objects[i] == null) { objects[i] = key; values[i] = value; return; } } /** * @param key * @return the hasvalue of a masked by the size of this table */ protected int hash(Object key) { return System.identityHashCode(key) % objects.length; } }