package x10.util; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.Map.Entry; public final class SmallMap<K,V> implements Map<K,V>, Cloneable { // we must support null key and values (TypeCheckFragmentGoal.runTask puts a null value) // keys[0] .. keys[curIndex-1] contains distinct elements, and there might be additional elements in overflow private Entry<K,V>[] entries; // because of entrySet().iterator(), it's more efficient to keep entries instead of two arrays of keys&values (which is more compact) private int curIndex; private LinkedHashMap<K,V> overflow = null; public SmallMap() { this(CollectionFactory.DEFAULT_SIZE); } public SmallMap(int size) { entries = (Entry<K,V>[])new Entry[size]; } public SmallMap(Map<K,V> map) { this(map.size()); putAll(map); } public SmallMap<K,V> clone() { try { return (SmallMap<K,V>) super.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block return null; } } public V put(K key, V value) { // if it's contained, then we need to change the value for (int i=0; i<curIndex; i++) { final Entry<K, V> en = entries[i]; if (CollectionFactory.equals(en.getKey(),key)) { V old = en.getValue(); en.setValue(value); return old; } } if (overflow!=null && overflow.containsKey(key)) return overflow.put(key,value); // it isn't contained, so let's add it if (curIndex<entries.length) { entries[curIndex++] = new Entry<K,V>(key,value); } else { if (overflow==null) overflow = new LinkedHashMap<K,V>(); overflow.put(key,value); } return null; } private void removeIndex(int i) { for (int j=i; j<curIndex-1; j++) entries[j] = entries[j+1]; entries[--curIndex] = null; // for garbage collection } public V remove(Object o) { for (int i=0; i<curIndex; i++) { final Entry<K, V> en = entries[i]; if (CollectionFactory.equals(en.getKey(),o)) { removeIndex(i); return en.getValue(); } } return overflow==null ? null : overflow.remove(o); } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Map<K,V> m = (Map<K,V>) o; if (m.size() != size()) return false; for (Map.Entry<K,V> en : m.entrySet()) if (!containsPair(en.getKey(),en.getValue())) return false; return true; } public void putAll(Map<? extends K, ? extends V> m) { if (m instanceof SmallMap) { SmallMap<K,V> s = (SmallMap) m; for (int i=0; i<s.curIndex; i++) { final Entry<K, V> en = s.entries[i]; put(en.getKey(),en.getValue()); } if (s.overflow!=null) for (Map.Entry<K,V> en : s.overflow.entrySet()) put(en.getKey(),en.getValue()); return; } for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } public boolean isEmpty() { return size()==0; } public int hashCode() { int h = 0; for (int i=0; i<curIndex; i++) h += entries[i].hashCode(); if (overflow!=null) h+=overflow.hashCode(); return h; } public V get(Object o) { for (int i=0; i<curIndex; i++) { final Entry<K, V> en = entries[i]; if (CollectionFactory.equals(en.getKey(),o)) return en.getValue(); } return overflow==null ? null : overflow.get(o); } public boolean containsKey(Object o) { for (int i=0; i<curIndex; i++) if (CollectionFactory.equals(entries[i].getKey(),o)) return true; return overflow==null ? false : overflow.containsKey(o); } public boolean containsValue(Object o) { for (int i=0; i<curIndex; i++) if (CollectionFactory.equals(entries[i].getValue(),o)) return true; return overflow==null ? false : overflow.containsValue(o); } public boolean containsPair(Object key, Object value) { for (int i=0; i<curIndex; i++) { final Entry<K, V> en = entries[i]; if (CollectionFactory.equals(en.getKey(),key) && CollectionFactory.equals(en.getValue(),value)) return true; } if (overflow==null) return false; if (value == null) { return overflow.get(key)==null && overflow.containsKey(key); } else { return value.equals(overflow.get(key)); } } public void clear() { for (int i=0; i<curIndex; i++) entries[i] = null; overflow = null; curIndex = 0; } public int size() { return curIndex+(overflow==null?0:overflow.size()); } // Code copied from AbstractMap. I don't want to inherit from AbstractMap because it has two fields (keySet, values) and it's a waste of space becasue we do not use them. public String toString() { StringBuffer buf = new StringBuffer(); buf.append("{"); Iterator<Map.Entry<K,V>> i = entrySet().iterator(); boolean hasNext = i.hasNext(); while (hasNext) { Map.Entry<K,V> e = i.next(); K key = e.getKey(); V value = e.getValue(); if (key == this) buf.append("(this Map)"); else buf.append(key); buf.append("="); if (value == this) buf.append("(this Map)"); else buf.append(value); hasNext = i.hasNext(); if (hasNext) buf.append(", "); } buf.append("}"); return buf.toString(); } static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; /** * Creates new entry. */ Entry(K k, V v) { value = v; key = k; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } return false; } public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } public final String toString() { return getKey() + "=" + getValue(); } } /** * Iterator part */ private abstract class SmallIterator<E> implements Iterator<E> { int pos; Iterator<Map.Entry<K,V>> overflowIt; public boolean hasNext() { return pos<curIndex || ( overflowIt==null ? (overflow!=null && !overflow.isEmpty()) : overflowIt.hasNext()); } public Map.Entry<K,V> nextEntry() { if (pos<curIndex) { return entries[pos++]; } else { if (overflowIt==null) overflowIt = overflow.entrySet().iterator(); return overflowIt.next(); } } public void remove() { if (overflowIt==null) { removeIndex(--pos); } else overflowIt.remove(); } } private final class ValueIterator extends SmallIterator<V> { public V next() { return nextEntry().getValue(); } } private final class KeyIterator extends SmallIterator<K> { public K next() { return nextEntry().getKey(); } } private final class EntryIterator extends SmallIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } private Set<Map.Entry<K,V>> entrySet = null; private Set<K> keySet = null; private Collection<V> valuesSet = null; public Set<Map.Entry<K, V>> entrySet() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } public Collection<V> values() { Collection<V> vs = valuesSet; return (vs != null ? vs : (valuesSet = new Values())); } public Set<K> keySet() { Set<K> ks = keySet; return (ks != null ? ks : (keySet = new KeySet())); } private final class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return new KeyIterator(); } public int size() { return SmallMap.this.size(); } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return SmallMap.this.remove(o) != null; } public void clear() { SmallMap.this.clear(); } } private final class Values extends java.util.AbstractCollection<V> { public Iterator<V> iterator() { return new ValueIterator(); } public int size() { return SmallMap.this.size(); } public boolean contains(Object o) { return containsValue(o); } public void clear() { SmallMap.this.clear(); } } private final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> en = (Map.Entry<K,V>) o; return containsPair(en.getKey(), en.getValue()); } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> en = (Map.Entry) o; final V value = en.getValue(); final K key = en.getKey(); boolean contained = containsPair(key, value); if (contained) remove(key); return contained; } public int size() { return SmallMap.this.size(); } public void clear() { SmallMap.this.clear(); } } }