You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sc...@apache.org on 2003/10/05 22:40:52 UTC

cvs commit: jakarta-commons/collections/src/java/org/apache/commons/collections HashBidiMap.java

scolebourne    2003/10/05 13:40:52

  Modified:    collections/src/java/org/apache/commons/collections
                        HashBidiMap.java
  Log:
  Refactor implementation to act as more of a decorator
  Fixed some bugs too
  
  Revision  Changes    Path
  1.4       +298 -165  jakarta-commons/collections/src/java/org/apache/commons/collections/HashBidiMap.java
  
  Index: HashBidiMap.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/collections/src/java/org/apache/commons/collections/HashBidiMap.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- HashBidiMap.java	29 Sep 2003 23:24:18 -0000	1.3
  +++ HashBidiMap.java	5 Oct 2003 20:40:52 -0000	1.4
  @@ -57,14 +57,16 @@
    */
   package org.apache.commons.collections;
   
  -import java.io.Serializable;
  -import java.util.AbstractMap;
  -import java.util.AbstractSet;
  +import java.util.Collection;
   import java.util.HashMap;
   import java.util.Iterator;
   import java.util.Map;
   import java.util.Set;
   
  +import org.apache.commons.collections.decorators.AbstractCollectionDecorator;
  +import org.apache.commons.collections.decorators.AbstractIteratorDecorator;
  +import org.apache.commons.collections.decorators.AbstractMapEntryDecorator;
  +
   /**
    * Default implementation of <code>BidiMap</code>.
    * 
  @@ -73,220 +75,351 @@
    * 
    * @author Matthew Hawthorne
    */
  -public class HashBidiMap extends AbstractMap implements BidiMap, Serializable {
  +public class HashBidiMap implements BidiMap {
   
       /**
        * Delegate map array.  The first map contains standard entries, and the 
        * second contains inverses.
        */
  -    final Map[] maps = new Map[] { new HashMap(), new HashMap()};
  -
  +    protected final Map[] maps = new Map[2];
       /**
        * Inverse view of this map.
        */
  -    private final BidiMap inverseBidiMap = new InverseBidiMap();
  +    protected BidiMap inverseBidiMap = null;
  +    /**
  +     * View of the keys.
  +     */
  +    protected Set keySet = null;
  +    /**
  +     * View of the values.
  +     */
  +    protected Collection values = null;
  +    /**
  +     * View of the entries.
  +     */
  +    protected Set entrySet = null;
   
       /**
        * Creates an empty <code>HashBidiMap</code>
        */
  -    public HashBidiMap() {}
  +    public HashBidiMap() {
  +        super();
  +        maps[0] = new HashMap();
  +        maps[1] = new HashMap();
  +    }
   
       /** 
  -     * Constructs a new <tt>HashMap</tt> with the same mappings as the
  -     * specified <tt>Map</tt>.  
  +     * Constructs a <code>HashBidiMap</code> and copies the mappings from
  +     * specified <code>Map</code>.  
        *
  -     * @param   m the map whose mappings are to be placed in this map.
  +     * @param map  the map whose mappings are to be placed in this map
        */
  -    public HashBidiMap(Map m) {
  -        putAll(m);
  +    public HashBidiMap(Map map) {
  +        super();
  +        maps[0] = new HashMap();
  +        maps[1] = new HashMap();
  +        putAll(map);
       }
   
  -    public Object getKey(Object value) {
  -        return maps[1].get(value);
  +    /** 
  +     * Constructs a <code>HashBidiMap</code> that decorates the specified maps.
  +     *
  +     * @param normalMap  the normal direction map
  +     * @param reverseMap  the reverse direction map
  +     * @param inverseBidiMap  the inverse BidiMap
  +     */
  +    protected HashBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
  +        super();
  +        maps[0] = normalMap;
  +        maps[1] = reverseMap;
  +        this.inverseBidiMap = inverseBidiMap;
       }
   
  -    public BidiMap inverseBidiMap() {
  -        return inverseBidiMap;
  +    // Map delegation
  +    //-----------------------------------------------------------------------
  +    public Object get(Object key) {
  +        return maps[0].get(key);
       }
   
  -    public Object removeKey(Object value) {
  -        final Object key = maps[1].get(value);
  -        return remove(key);
  +    public int size() {
  +        return maps[0].size();
       }
   
  -    public Object put(Object key, Object value) {
  -        // Removes pair from standard map if a previous inverse entry exists
  -        final Object oldValue = maps[1].put(value, key);
  -        if (oldValue != null) {
  -            maps[0].remove(oldValue);
  -        }
  -
  -        final Object obj = maps[0].put(key, value);
  -        return obj;
  +    public boolean isEmpty() {
  +        return maps[0].isEmpty();
       }
   
  -    public Set entrySet() {
  -        // The entrySet is the root of most Map methods, care must be taken not 
  -        // to reference instance methods like size()
  -
  -        // Creates anonymous AbstractSet
  -        return new AbstractSet() {
  -
  -            public Iterator iterator() {
  -                // Creates anonymous Iterator
  -                return new Iterator() {
  +    public boolean containsKey(Object key) {
  +        return maps[0].containsKey(key);
  +    }
   
  -                    // Delegate iterator.
  -                    final Iterator it = maps[0].entrySet().iterator();
  +    public boolean equals(Object obj) {
  +        return maps[0].equals(obj);
  +    }
   
  -                    // Current iterator entry
  -                    Map.Entry currentEntry;
  +    public int hashCode() {
  +        return maps[0].hashCode();
  +    }
   
  -                    public void remove() {
  -                        // Removes from standard and inverse Maps.
  +    public String toString() {
  +        return maps[0].toString();
  +    }
   
  -                        // Object must be removed using the iterator or a 
  -                        // ConcurrentModificationException is thrown
  -                        it.remove();
  -                        HashBidiMap.this.maps[1].remove(
  -                            currentEntry.getValue());
  -                    }
  +    // BidiMap changes
  +    //-----------------------------------------------------------------------
  +    public Object put(Object key, Object value) {
  +        if (maps[0].containsKey(key)) {
  +            maps[1].remove(maps[0].get(key));
  +        }
  +        if (maps[1].containsKey(value)) {
  +            maps[0].remove(maps[1].get(value));
  +        }
  +        final Object obj = maps[0].put(key, value);
  +        maps[1].put(value, key);
  +        return obj;
  +    }
  +    
  +    public void putAll(Map map) {
  +        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
  +            Map.Entry entry = (Map.Entry) it.next();
  +            put(entry.getKey(), entry.getValue());
  +        }
  +    }
   
  -                    public boolean hasNext() {
  -                        return it.hasNext();
  -                    }
  +    public Object remove(Object key) {
  +        Object value = null;
  +        if (maps[0].containsKey(key)) {
  +            value = maps[0].remove(key);
  +            maps[1].remove(value);
  +        }
  +        return value;
  +    }
   
  -                    public Object next() {
  -                        currentEntry = (Map.Entry)it.next();
  +    public void clear() {
  +        maps[0].clear();
  +        maps[1].clear();
  +    }
   
  -                        // returns anonymous Map.Entry
  -                        return new Map.Entry() {
  +    public boolean containsValue(Object value) {
  +        return maps[1].containsKey(value);
  +    }
   
  -                            public Object getKey() {
  -                                return currentEntry.getKey();
  -                            }
  +    // BidiMap
  +    //-----------------------------------------------------------------------
  +    public Object getKey(Object value) {
  +        return maps[1].get(value);
  +    }
   
  -                            public Object getValue() {
  -                                return currentEntry.getValue();
  -                            }
  +    public Object removeKey(Object value) {
  +        Object key = null;
  +        if (maps[1].containsKey(value)) {
  +            key = maps[1].remove(value);
  +            maps[0].remove(key);
  +        }
  +        return key;
  +    }
   
  -                            public Object setValue(Object value) {
  -                                final Object oldValue =
  -                                    currentEntry.setValue(value);
  +    public BidiMap inverseBidiMap() {
  +        if (inverseBidiMap == null) {
  +            inverseBidiMap = new HashBidiMap(maps[1], maps[0], this);
  +        }
  +        return inverseBidiMap;
  +    }
   
  -                                // Gets old key and pairs with new value
  -                                final Object inverseKey =
  -                                    HashBidiMap.this.maps[1].remove(oldValue);
  -                                HashBidiMap.this.maps[1].put(value, inverseKey);
  +    // Map views
  +    //-----------------------------------------------------------------------
  +    public Set keySet() {
  +        if (keySet == null) {
  +            keySet = new KeySet(this);
  +        }
  +        return keySet;
  +    }
   
  -                                return oldValue;
  -                            }
  +    public Collection values() {
  +        if (values == null) {
  +            values = new Values(this);
  +        }
  +        return values;
  +    }
   
  -                        }; // anonymous Map.Entry
  -                    };
  -                }; // anonymous Iterator
  -            }
  +    public Set entrySet() {
  +        if (entrySet == null) {
  +            entrySet = new EntrySet(this);
  +        }
  +        return entrySet;
  +    }
  +    
  +    //-----------------------------------------------------------------------
  +    /**
  +     * Inner class View.
  +     */
  +    protected static abstract class View extends AbstractCollectionDecorator {
  +        
  +        protected final HashBidiMap map;
  +        
  +        protected View(Collection coll, HashBidiMap map) {
  +            super(coll);
  +            this.map = map;
  +        }
   
  -            public boolean remove(Object obj) {
  -                // XXX Throws ClassCastException if obj is not a Map.Entry.
  -                // Is this acceptable?
  -                final Object removed =
  -                    HashBidiMap.this.remove(((Map.Entry)obj).getKey());
  -                return removed != null;
  +        public boolean removeAll(Collection coll) {
  +            boolean modified = false;
  +            Iterator it = iterator();
  +            while (it.hasNext()) {
  +                if (coll.contains(it.next())) {
  +                    it.remove();
  +                    modified = true;
  +                }
               }
  +            return modified;
  +        }
   
  -            public int size() {
  -                return HashBidiMap.this.maps[0].size();
  +        public boolean retainAll(Collection coll) {
  +            boolean modified = false;
  +            Iterator it = iterator();
  +            while (it.hasNext()) {
  +                if (coll.contains(it.next()) == false) {
  +                    it.remove();
  +                    modified = true;
  +                }
               }
  +            return modified;
  +        }
   
  -        }; // anonymous AbstractSet
  -
  -    } // entrySet()
  -
  +        public void clear() {
  +            map.clear();
  +        }
  +    }
  +    
       /**
  -     * Inverse view of this BidiMap.
  +     * Inner class KeySet.
        */
  -    private final class InverseBidiMap extends AbstractMap implements BidiMap {
  -
  -        public Object getKey(Object value) {
  -            return HashBidiMap.this.get(value);
  +    protected static class KeySet extends View implements Set {
  +        
  +        protected KeySet(HashBidiMap map) {
  +            super(map.maps[0].keySet(), map);
           }
   
  -        public BidiMap inverseBidiMap() {
  -            return HashBidiMap.this;
  -        }
  +        public Iterator iterator() {
  +            return new AbstractIteratorDecorator(super.iterator()) {
  +                private Object last;
  +                
  +                public Object next() {
  +                    last = super.next();
  +                    return last;
  +                }
   
  -        public Object removeKey(Object value) {
  -            return HashBidiMap.this.remove(value);
  +                public void remove() {
  +                    Object value = map.maps[0].get(last);
  +                    super.remove();
  +                    map.maps[1].remove(value);
  +                }
  +            };
  +        }
  +        
  +        public boolean remove(Object key) {
  +            if (contains(key)) {
  +                Object value = map.maps[0].remove(key);
  +                map.maps[1].remove(value);
  +                return true;
  +            }
  +            return false;
  +        }
  +    }
  +    
  +    /**
  +     * Inner class Values.
  +     */
  +    protected static class Values extends View {
  +        
  +        protected Values(HashBidiMap map) {
  +            super(map.maps[0].values(), map);
           }
   
  -        public Set entrySet() {
  -            // Gets entry set from outer class
  -            final Set entrySet = HashBidiMap.this.entrySet();
  -
  -            // Returns anonymous Set
  -            return new AbstractSet() {
  -
  -                public int size() {
  -                    return HashBidiMap.this.size();
  +        public Iterator iterator() {
  +            return new AbstractIteratorDecorator(super.iterator()) {
  +                private Object last;
  +                
  +                public Object next() {
  +                    last = super.next();
  +                    return last;
                   }
   
  -                public Iterator iterator() {
  -                    final Iterator delegate = entrySet.iterator();
  -
  -                    // Returns anonymous Iterator
  -                    return new Iterator() {
  -
  -                        public boolean hasNext() {
  -                            return delegate.hasNext();
  -                        }
  -
  -                        public Object next() {
  -                            final Map.Entry entry = (Map.Entry)delegate.next();
  -
  -                            // Returns anonymous Map.Entry
  -                            return new Map.Entry() {
  -
  -                                public Object getKey() {
  -                                    return entry.getValue();
  -                                }
  -
  -                                public Object getValue() {
  -                                    return entry.getKey();
  -                                }
  -
  -                                public Object setValue(Object value) {
  -                                    // This is confusing.  Basically, we are 
  -                                    // setting a new key for existing value
  -                                    
  -                                    // Gets value for current key
  -                                    final Object oldValue =
  -                                        HashBidiMap.this.maps[0].remove(getValue());
  -                                    
  -                                    // Puts new key and value into map    
  -                                    HashBidiMap.this.maps[0].put(
  -                                        value,
  -                                        oldValue);
  -                                        
  -                                    // Returns old value
  -                                    return oldValue;
  -                                }
  -
  -                            }; // anonymous Map.Entry
  -
  -                        }
  -
  -                        public void remove() {
  -                            delegate.remove();
  -                        }
  -
  -                    }; // anonymous Iterator
  +                public void remove() {
  +                    super.remove();
  +                    map.maps[1].remove(last);
                   }
  +            };
  +        }
  +        
  +        public boolean remove(Object value) {
  +            if (contains(value)) {
  +                Object key = map.maps[1].remove(value);
  +                map.maps[0].remove(key);
  +                return true;
  +            }
  +            return false;
  +        }
  +        
  +    }
  +    
  +    /**
  +     * Inner class EntrySet.
  +     */
  +    protected static class EntrySet extends View implements Set {
  +        
  +        protected EntrySet(HashBidiMap map) {
  +            super(map.maps[0].entrySet(), map);
  +        }
   
  -            }; // anonymous AbstractSet
  -
  -        } // entrySet()
  +        public Iterator iterator() {
  +            return new AbstractIteratorDecorator(super.iterator()) {
  +                private Map.Entry last;
  +                
  +                public Object next() {
  +                    last = new MapEntry((Map.Entry) super.next(), map);
  +                    return last;
  +                }
   
  -    } // InverseBidiMap
  +                public void remove() {
  +                    super.remove();
  +                    map.maps[0].remove(last.getValue());
  +                }
  +            };
  +        }
  +        
  +        public boolean remove(Object obj) {
  +            if (obj instanceof Map.Entry == false) {
  +                return false;
  +            }
  +            Map.Entry entry = (Map.Entry) obj;
  +            if (map.containsKey(entry.getKey())) {
  +                Object value = map.maps[0].remove(entry.getKey());
  +                map.maps[1].remove(value);
  +                return true;
  +            }
  +            return false;
  +        }
  +    }
  +    
  +    protected static class MapEntry extends AbstractMapEntryDecorator {
  +        
  +        protected final HashBidiMap map;
  +        
  +        protected MapEntry(Map.Entry entry, HashBidiMap map) {
  +            super(entry);
  +            this.map = map;
  +        }
  +        
  +        public Object setValue(Object value) {
  +            final Object oldValue = super.setValue(value);
  +
  +            // Gets old key and pairs with new value
  +            final Object inverseKey = map.maps[1].remove(oldValue);
  +            map.maps[1].put(value, inverseKey);
   
  -} // HashBidiMap
  +            return oldValue;
  +        }
  +    }
  +    
  +}
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org