//package net.ontopia.utils; 
 
import java.util.AbstractCollection; 
import java.util.AbstractMap; 
import java.util.AbstractSet; 
import java.util.Collection; 
import java.util.Iterator; 
import java.util.NoSuchElementException; 
import java.util.Set; 
 
public class CompactHashMap<K, V> extends AbstractMap<K, V> { 
  protected final static int INITIAL_SIZE = 3; 
  protected final static double LOAD_FACTOR = 0.6; 
 
  /** 
   * This object is used to represent null, should clients use that as a key. 
   */ 
  protected final static Object nullObject = new Object(); 
  /** 
   * When a key is deleted this object is put into the hashtable in its place, 
   * so that other entries with the same key (collisions) further down the 
   * hashtable are not lost after we delete an object in the collision chain. 
   */ 
  protected final static Object deletedObject = new Object(); 
  protected int elements; 
  protected int freecells; 
  protected K[] keys; 
  protected V[] values; // object at pos x corresponds to key at pos x 
  protected int modCount; 
 
  /** 
   * Constructs a new, empty set. 
   */ 
  public CompactHashMap() { 
    this(INITIAL_SIZE); 
  } 
 
  /** 
   * Constructs a new, empty set. 
   */ 
  public CompactHashMap(int size) { 
    // NOTE: If array size is 0, we get a 
    // "java.lang.ArithmeticException: / by zero" in add(Object). 
    keys = (K[]) new Object[(size == 0 ? 1 : size)]; 
    values = (V[]) new Object[(size == 0 ? 1 : size)]; 
    elements = 0; 
    freecells = keys.length; 
    modCount = 0; 
  } 
 
  // MAP IMPLEMENTATION  
 
  /** 
   * Returns the number of key/value mappings in this map. 
   */ 
  public int size() { 
    return elements; 
  } 
 
  /** 
   * Returns <tt>true</tt> if this map contains no mappings. 
   */ 
  public boolean isEmpty() { 
    return elements == 0; 
  } 
 
  /** 
   * Removes all key/value mappings in the map. 
   */ 
  public void clear() { 
    elements = 0; 
    for (int ix = 0; ix < keys.length; ix++) { 
      keys[ix] = null; 
      values[ix] = null; 
    } 
    freecells = values.length; 
    modCount++; 
  } 
 
  /** 
   * Returns <tt>true</tt> if this map contains the specified key. 
   */ 
  public boolean containsKey(Object k) { 
    return keys[findKeyIndex(k)] != null; 
  } 
 
  /** 
   * Returns <tt>true</tt> if this map contains the specified value. 
   */ 
  public boolean containsValue(Object v) { 
    if (v == null) 
      v = (V) nullObject; 
 
    for (int ix = 0; ix < values.length; ix++) 
      if (values[ix] != null && values[ix].equals(v)) 
        return true; 
 
    return false; 
  } 
 
  /** 
   * Returns a read-only set view of the map's keys. 
   */ 
  public Set<Entry<K, V>> entrySet() { 
    throw new UnsupportedOperationException(); 
  } 
 
  /** 
   * Removes the mapping with key k, if there is one, and returns its value, 
   * if there is one, and null if there is none. 
   */ 
  public V remove(Object k) { 
    int index = findKeyIndex(k); 
 
    // we found the right position, now do the removal 
    if (keys[index] != null) { 
      // we found the object 
 
      // same problem here as with put 
      V v = values[index]; 
      keys[index] = (K) deletedObject; 
      values[index] = (V) deletedObject; 
      modCount++; 
      elements--; 
      return v; 
    } else 
      // we did not find the key 
      return null; 
  } 
 
  /** 
   * Adds the specified mapping to this map, returning the old value for the 
   * mapping, if there was one. 
   */ 
  public V put(K k, V v) { 
    if (k == null) 
      k = (K) nullObject; 
 
    int hash = k.hashCode(); 
    int index = (hash & 0x7FFFFFFF) % keys.length; 
    int offset = 1; 
    int deletedix = -1; 
 
    // search for the key (continue while !null and !this key) 
    while (keys[index] != null 
        && !(keys[index].hashCode() == hash && keys[index].equals(k))) { 
 
      // if there's a deleted mapping here we can put this mapping here, 
      // provided it's not in here somewhere else already 
      if (keys[index] == deletedObject) 
        deletedix = index; 
 
      index = ((index + offset) & 0x7FFFFFFF) % keys.length; 
      offset = offset * 2 + 1; 
 
      if (offset == -1) 
        offset = 2; 
    } 
 
    if (keys[index] == null) { // wasn't present already 
      if (deletedix != -1) // reusing a deleted cell 
        index = deletedix; 
      else 
        freecells--; 
 
      modCount++; 
      elements++; 
 
      keys[index] = (K) k; 
      values[index] = (V) v; 
 
      // rehash with increased capacity 
      if (1 - (freecells / (double) keys.length) > LOAD_FACTOR) 
        rehash(keys.length * 2 + 1); 
      return null; 
    } else { // was there already 
      modCount++; 
      V oldv = values[index]; 
      values[index] = (V) v; 
      return oldv; 
    } 
  } 
 
  /** 
   * INTERNAL: Rehashes the hashmap to a bigger size. 
   */ 
  protected void rehash(int newCapacity) { 
    int oldCapacity = keys.length; 
    K[] newKeys = (K[]) new Object[newCapacity]; 
    V[] newValues = (V[]) new Object[newCapacity]; 
 
    for (int ix = 0; ix < oldCapacity; ix++) { 
      Object k = keys[ix]; 
      if (k == null || k == deletedObject) 
        continue; 
 
      int hash = k.hashCode(); 
      int index = (hash & 0x7FFFFFFF) % newCapacity; 
      int offset = 1; 
 
      // search for the key 
      while (newKeys[index] != null) { // no need to test for duplicates 
        index = ((index + offset) & 0x7FFFFFFF) % newCapacity; 
        offset = offset * 2 + 1; 
 
        if (offset == -1) 
          offset = 2; 
      } 
 
      newKeys[index] = (K) k; 
      newValues[index] = values[ix]; 
    } 
 
    keys = newKeys; 
    values = newValues; 
    freecells = keys.length - elements; 
  } 
 
  /** 
   * Returns the value for the key k, if there is one, and null if there is 
   * none. 
   */ 
  public V get(Object k) { 
    return values[findKeyIndex(k)]; 
  } 
 
  /** 
   * Returns a virtual read-only collection containing all the values in the 
   * map. 
   */ 
  public Collection<V> values() { 
    return new ValueCollection(); 
  } 
 
  /** 
   * Returns a virtual read-only set of all the keys in the map. 
   */ 
  public Set<K> keySet() { 
    return new KeySet(); 
  } 
 
  // --- Internal utilities 
 
  private final int findKeyIndex(Object k) { 
    if (k == null) 
      k = nullObject; 
 
    int hash = k.hashCode(); 
    int index = (hash & 0x7FFFFFFF) % keys.length; 
    int offset = 1; 
 
    // search for the key (continue while !null and !this key) 
    while (keys[index] != null 
        && !(keys[index].hashCode() == hash && keys[index].equals(k))) { 
      index = ((index + offset) & 0x7FFFFFFF) % keys.length; 
      offset = offset * 2 + 1; 
 
      if (offset == -1) 
        offset = 2; 
    } 
    return index; 
  } 
 
  // --- Key set 
 
  private class KeySet<K> extends AbstractSet<K> { 
    public int size() { 
      return elements; 
    } 
 
    public boolean contains(Object k) { 
      return containsKey(k); 
    } 
 
    public Iterator<K> iterator() { 
      return new KeyIterator(); 
    } 
  } 
 
  private class KeyIterator<K> implements Iterator<K> { 
    private int ix; 
 
    private KeyIterator() { 
      // walk up to first value, so that hasNext() and next() return 
      // correct results 
      for (; ix < keys.length; ix++) 
        if (values[ix] != null && keys[ix] != deletedObject) 
          break; 
    } 
 
    public boolean hasNext() { 
      return ix < keys.length; 
    } 
 
    public void remove() { 
      throw new UnsupportedOperationException("Collection is read-only"); 
    } 
 
    public K next() { 
      if (ix >= keys.length) 
        throw new NoSuchElementException(); 
      K key = (K) keys[ix++]; 
 
      // walk up to next value 
      for (; ix < keys.length; ix++) 
        if (keys[ix] != null && keys[ix] != deletedObject) 
          break; 
 
      // ix now either points to next key, or outside array (if no next) 
      return key; 
    } 
  } 
 
  // --- Value collection 
 
  private class ValueCollection<V> extends AbstractCollection<V> { 
    public int size() { 
      return elements; 
    } 
 
    public Iterator<V> iterator() { 
      return new ValueIterator(); 
    } 
 
    public boolean contains(Object v) { 
      return containsValue(v); 
    } 
  } 
 
  private class ValueIterator<V> implements Iterator<V> { 
    private int ix; 
 
    private ValueIterator() { 
      // walk up to first value, so that hasNext() and next() return 
      // correct results 
      for (; ix < values.length; ix++) 
        if (values[ix] != null && values[ix] != deletedObject) 
          break; 
    } 
 
    public boolean hasNext() { 
      return ix < values.length; 
    } 
 
    public void remove() { 
      throw new UnsupportedOperationException("Collection is read-only"); 
    } 
 
    public V next() { 
      if (ix >= values.length) 
        throw new NoSuchElementException(); 
      V value = (V) values[ix++]; 
 
      // walk up to next value 
      for (; ix < values.length; ix++) 
        if (values[ix] != null && values[ix] != deletedObject) 
          break; 
 
      // ix now either points to next value, or outside array (if no next) 
      return value; 
    } 
  } 
} 
 
    
     
     
  
  |