码迷,mamicode.com
首页 > Web开发 > 详细

apache_commons 之 双向Map DualHashBidiMap (使用及源码)

时间:2016-12-30 13:20:40      阅读:334      评论:0      收藏:0      [点我收藏+]

标签:hash   hang   size   tin   创建   reflect   ati   which   获取   

技术分享

在项目当中,经常出现需要根据Key值获取value;而且要求根据value获取key值,其实在commons-collections包中已经提供了此集合类。就是DualHashBidiMap类。

(官方API:http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/BidiMap.html)

其用法如下:

/**
 * BidiMap,双向Map,可以通过key找到value, 也可以通过value找到key,<br>
 * 注意:BidiMap中key不能重复,value也不可以。
 *
 * 实现思路为:使用了两个Map,其中一个作为Key-Value使用,另外一个在put时,<br/>
 * 把value做为key,key作为value。<br/>
 * 当根据key查询value时,使用第一个map,此时和普通Map一样;<br/>
 * 当根据value查询key时,使用第二个map,也即查询的key就是put时的value值。
 *
 * 使用此Map需要引入commons-collections的jar包
 * @auther <a href="mailto:g2room@163.com">天涯</a>
 * May 31, 2013 2:32:55 PM
 */
public static void bidiMap() {
    // 第一种用法,和Map用法一样
    // 此时实际创建了两个临时Map,分别记录key-value和value-key
    // 根据key查询value时,使用key-value的map;
    // 根据value查询key时,使用value-key的map。
    DualHashBidiMap dualHashBidiMap = new DualHashBidiMap();
    dualHashBidiMap.put("success", "成功");
    dualHashBidiMap.put("fail", "失败");
    dualHashBidiMap.put("wait", "等待");
    System.out.println("根据Key获取Value: fail = " + dualHashBidiMap.get("fail"));
    System.out.println("根据Value获取Key: 等待 = " + dualHashBidiMap.getKey("等待"));
    Map<String, String> student1 = new HashMap<String, String>();
    student1.put("name", "张三");
    student1.put("score", "83");
    student1.put("sex", "男");
    // 第二种用法,让已有的Map也能通过value查找到key
    // 此时实际创建了两个临时Map,分别记录key-value和value-key
    // 但传入的Map会复制到临时的第一个Map里面
    // 根据key查询value时,使用key-value的map;
    // 根据value查询key时,使用value-key的map。
    dualHashBidiMap = new DualHashBidiMap(student1);
    System.out.println("根据Key获取Value: name = " + dualHashBidiMap.get("name"));
    System.out.println("根据Value获取Key: 男 = " + dualHashBidiMap.getKey("男"));
}

输出结果如下:
      根据Key获取Value: fail = 失败
      根据Value获取Key: 等待 = wait
      根据Key获取Value: name = 张三
      根据Value获取Key: 男 = sex

 DualHashBidiMap实现源码如下:

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.commons.collections.bidimap;
 
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
 
import org.apache.commons.collections.BidiMap;
 
/**
 * Implementation of <code>BidiMap</code> that uses two <code>HashMap</code> instances.
 * <p>
 * Two <code>HashMap</code> instances are used in this class.
 * This provides fast lookups at the expense of storing two sets of map entries.
 * Commons Collections would welcome the addition of a direct hash-based
 * implementation of the <code>BidiMap</code> interface.
 * <p>
 * NOTE: From Commons Collections 3.1, all subclasses will use <code>HashMap</code>
 * and the flawed <code>createMap</code> method is ignored.
 *
 * @since Commons Collections 3.0
 * @version $Id: DualHashBidiMap.java 646777 2008-04-10 12:33:15Z niallp $
 *
 * @author Matthew Hawthorne
 * @author Stephen Colebourne
 */
public class DualHashBidiMap
        extends AbstractDualBidiMap implements Serializable {
 
    /** Ensure serialization compatibility */
    private static final long serialVersionUID = 721969328361808L;
 
    /**
     * Creates an empty <code>HashBidiMap</code>.
     */
    public DualHashBidiMap() {
        super(new HashMap(), new HashMap());
    }
 
    /**
     * Constructs a <code>HashBidiMap</code> and copies the mappings from
     * specified <code>Map</code>. 
     *
     * @param map  the map whose mappings are to be placed in this map
     */
    public DualHashBidiMap(Map map) {
        super(new HashMap(), new HashMap());
        putAll(map);
    }
     
    /**
     * 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 DualHashBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
        super(normalMap, reverseMap, inverseBidiMap);
    }
 
    /**
     * Creates a new instance of this object.
     *
     * @param normalMap  the normal direction map
     * @param reverseMap  the reverse direction map
     * @param inverseBidiMap  the inverse BidiMap
     * @return new bidi map
     */
    protected BidiMap createBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
        return new DualHashBidiMap(normalMap, reverseMap, inverseBidiMap);
    }
 
    // Serialization
    //-----------------------------------------------------------------------
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeObject(maps[0]);
    }
 
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        maps[0] = new HashMap();
        maps[1] = new HashMap();
        Map map = (Map) in.readObject();
        putAll(map);
    }
}

   其抽象类AbstractDualBidiMap实现源码如下:

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.commons.collections.bidimap;
 
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
 
import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.MapIterator;
import org.apache.commons.collections.ResettableIterator;
import org.apache.commons.collections.collection.AbstractCollectionDecorator;
import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
 
/**
 * Abstract <code>BidiMap</code> implemented using two maps.
 * <p>
 * An implementation can be written simply by implementing the
 * <code>createMap</code> method.
 *
 * @see DualHashBidiMap
 * @see DualTreeBidiMap
 * @since Commons Collections 3.0
 * @version $Id: AbstractDualBidiMap.java 646777 2008-04-10 12:33:15Z niallp $
 *
 * @author Matthew Hawthorne
 * @author Stephen Colebourne
 */
public abstract class AbstractDualBidiMap implements BidiMap {
 
    /**
     * Delegate map array.  The first map contains standard entries, and the
     * second contains inverses.
     */
    protected transient final Map[] maps = new Map[2];
    /**
     * Inverse view of this map.
     */
    protected transient BidiMap inverseBidiMap = null;
    /**
     * View of the keys.
     */
    protected transient Set keySet = null;
    /**
     * View of the values.
     */
    protected transient Collection values = null;
    /**
     * View of the entries.
     */
    protected transient Set entrySet = null;
 
    /**
     * Creates an empty map, initialised by <code>createMap</code>.
     * <p>
     * This constructor remains in place for deserialization.
     * All other usage is deprecated in favour of
     * {@link #AbstractDualBidiMap(Map, Map)}.
     */
    protected AbstractDualBidiMap() {
        super();
        maps[0] = createMap();
        maps[1] = createMap();
    }
 
    /**
     * Creates an empty map using the two maps specified as storage.
     * <p>
     * The two maps must be a matching pair, normal and reverse.
     * They will typically both be empty.
     * <p>
     * Neither map is validated, so nulls may be passed in.
     * If you choose to do this then the subclass constructor must populate
     * the <code>maps[]</code> instance variable itself.
     *
     * @param normalMap  the normal direction map
     * @param reverseMap  the reverse direction map
     * @since Commons Collections 3.1
     */
    protected AbstractDualBidiMap(Map normalMap, Map reverseMap) {
        super();
        maps[0] = normalMap;
        maps[1] = reverseMap;
    }
 
    /**
     * Constructs a map that decorates the specified maps,
     * used by the subclass <code>createBidiMap</code> implementation.
     *
     * @param normalMap  the normal direction map
     * @param reverseMap  the reverse direction map
     * @param inverseBidiMap  the inverse BidiMap
     */
    protected AbstractDualBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
        super();
        maps[0] = normalMap;
        maps[1] = reverseMap;
        this.inverseBidiMap = inverseBidiMap;
    }
 
    /**
     * Creates a new instance of the map used by the subclass to store data.
     * <p>
     * This design is deeply flawed and has been deprecated.
     * It relied on subclass data being used during a superclass constructor.
     *
     * @return the map to be used for internal storage
     * @deprecated For constructors, use the new two map constructor.
     * For deserialization, populate the maps array directly in readObject.
     */
    protected Map createMap() {
        return null;
    }
 
    /**
     * Creates a new instance of the subclass.
     *
     * @param normalMap  the normal direction map
     * @param reverseMap  the reverse direction map
     * @param inverseMap  this map, which is the inverse in the new map
     * @return the inverse map
     */
    protected abstract BidiMap createBidiMap(Map normalMap, Map reverseMap, BidiMap inverseMap);
 
    // Map delegation
    //-----------------------------------------------------------------------
    public Object get(Object key) {
        return maps[0].get(key);
    }
 
    public int size() {
        return maps[0].size();
    }
 
    public boolean isEmpty() {
        return maps[0].isEmpty();
    }
 
    public boolean containsKey(Object key) {
        return maps[0].containsKey(key);
    }
 
    public boolean equals(Object obj) {
        return maps[0].equals(obj);
    }
 
    public int hashCode() {
        return maps[0].hashCode();
    }
 
    public String toString() {
        return maps[0].toString();
    }
 
    // 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 Object remove(Object key) {
        Object value = null;
        if (maps[0].containsKey(key)) {
            value = maps[0].remove(key);
            maps[1].remove(value);
        }
        return value;
    }
 
    public void clear() {
        maps[0].clear();
        maps[1].clear();
    }
 
    public boolean containsValue(Object value) {
        return maps[1].containsKey(value);
    }
 
    // BidiMap
    //-----------------------------------------------------------------------
    /**
     * Obtains a <code>MapIterator</code> over the map.
     * The iterator implements <code>ResetableMapIterator</code>.
     * This implementation relies on the entrySet iterator.
     * <p>
     * The setValue() methods only allow a new value to be set.
     * If the value being set is already in the map, an IllegalArgumentException
     * is thrown (as setValue cannot change the size of the map).
     *
     * @return a map iterator
     */
    public MapIterator mapIterator() {
        return new BidiMapIterator(this);
    }
     
    public Object getKey(Object value) {
        return maps[1].get(value);
    }
 
    public Object removeValue(Object value) {
        Object key = null;
        if (maps[1].containsKey(value)) {
            key = maps[1].remove(value);
            maps[0].remove(key);
        }
        return key;
    }
 
    public BidiMap inverseBidiMap() {
        if (inverseBidiMap == null) {
            inverseBidiMap = createBidiMap(maps[1], maps[0], this);
        }
        return inverseBidiMap;
    }
     
    // Map views
    //-----------------------------------------------------------------------
    /**
     * Gets a keySet view of the map.
     * Changes made on the view are reflected in the map.
     * The set supports remove and clear but not add.
     *
     * @return the keySet view
     */
    public Set keySet() {
        if (keySet == null) {
            keySet = new KeySet(this);
        }
        return keySet;
    }
 
    /**
     * Creates a key set iterator.
     * Subclasses can override this to return iterators with different properties.
     *
     * @param iterator  the iterator to decorate
     * @return the keySet iterator
     */
    protected Iterator createKeySetIterator(Iterator iterator) {
        return new KeySetIterator(iterator, this);
    }
 
    /**
     * Gets a values view of the map.
     * Changes made on the view are reflected in the map.
     * The set supports remove and clear but not add.
     *
     * @return the values view
     */
    public Collection values() {
        if (values == null) {
            values = new Values(this);
        }
        return values;
    }
 
    /**
     * Creates a values iterator.
     * Subclasses can override this to return iterators with different properties.
     *
     * @param iterator  the iterator to decorate
     * @return the values iterator
     */
    protected Iterator createValuesIterator(Iterator iterator) {
        return new ValuesIterator(iterator, this);
    }
 
    /**
     * Gets an entrySet view of the map.
     * Changes made on the set are reflected in the map.
     * The set supports remove and clear but not add.
     * <p>
     * The Map Entry setValue() method only allow a new value to be set.
     * If the value being set is already in the map, an IllegalArgumentException
     * is thrown (as setValue cannot change the size of the map).
     *
     * @return the entrySet view
     */
    public Set entrySet() {
        if (entrySet == null) {
            entrySet = new EntrySet(this);
        }
        return entrySet;
    }
     
    /**
     * Creates an entry set iterator.
     * Subclasses can override this to return iterators with different properties.
     *
     * @param iterator  the iterator to decorate
     * @return the entrySet iterator
     */
    protected Iterator createEntrySetIterator(Iterator iterator) {
        return new EntrySetIterator(iterator, this);
    }
 
    //-----------------------------------------------------------------------
    /**
     * Inner class View.
     */
    protected static abstract class View extends AbstractCollectionDecorator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
         
        /**
         * Constructs a new view of the BidiMap.
         *
         * @param coll  the collection view being decorated
         * @param parent  the parent BidiMap
         */
        protected View(Collection coll, AbstractDualBidiMap parent) {
            super(coll);
            this.parent = parent;
        }
 
        public boolean removeAll(Collection coll) {
            if (parent.isEmpty() || coll.isEmpty()) {
                return false;
            }
            boolean modified = false;
            Iterator it = iterator();
            while (it.hasNext()) {
                if (coll.contains(it.next())) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }
 
        public boolean retainAll(Collection coll) {
            if (parent.isEmpty()) {
                return false;
            }
            if (coll.isEmpty()) {
                parent.clear();
                return true;
            }
            boolean modified = false;
            Iterator it = iterator();
            while (it.hasNext()) {
                if (coll.contains(it.next()) == false) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }
         
        public void clear() {
            parent.clear();
        }
    }
     
    //-----------------------------------------------------------------------
    /**
     * Inner class KeySet.
     */
    protected static class KeySet extends View implements Set {
         
        /**
         * Constructs a new view of the BidiMap.
         *
         * @param parent  the parent BidiMap
         */
        protected KeySet(AbstractDualBidiMap parent) {
            super(parent.maps[0].keySet(), parent);
        }
 
        public Iterator iterator() {
            return parent.createKeySetIterator(super.iterator());
        }
         
        public boolean contains(Object key) {
            return parent.maps[0].containsKey(key);
        }
 
        public boolean remove(Object key) {
            if (parent.maps[0].containsKey(key)) {
                Object value = parent.maps[0].remove(key);
                parent.maps[1].remove(value);
                return true;
            }
            return false;
        }
    }
     
    /**
     * Inner class KeySetIterator.
     */
    protected static class KeySetIterator extends AbstractIteratorDecorator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
        /** The last returned key */
        protected Object lastKey = null;
        /** Whether remove is allowed at present */
        protected boolean canRemove = false;
         
        /**
         * Constructor.
         * @param iterator  the iterator to decorate
         * @param parent  the parent map
         */
        protected KeySetIterator(Iterator iterator, AbstractDualBidiMap parent) {
            super(iterator);
            this.parent = parent;
        }
         
        public Object next() {
            lastKey = super.next();
            canRemove = true;
            return lastKey;
        }
         
        public void remove() {
            if (canRemove == false) {
                throw new IllegalStateException("Iterator remove() can only be called once after next()");
            }
            Object value = parent.maps[0].get(lastKey);
            super.remove();
            parent.maps[1].remove(value);
            lastKey = null;
            canRemove = false;
        }
    }
 
    //-----------------------------------------------------------------------
    /**
     * Inner class Values.
     */
    protected static class Values extends View implements Set {
         
        /**
         * Constructs a new view of the BidiMap.
         *
         * @param parent  the parent BidiMap
         */
        protected Values(AbstractDualBidiMap parent) {
            super(parent.maps[0].values(), parent);
        }
 
        public Iterator iterator() {
            return parent.createValuesIterator(super.iterator());
        }
         
        public boolean contains(Object value) {
            return parent.maps[1].containsKey(value);
        }
 
        public boolean remove(Object value) {
            if (parent.maps[1].containsKey(value)) {
                Object key = parent.maps[1].remove(value);
                parent.maps[0].remove(key);
                return true;
            }
            return false;
        }
    }
     
    /**
     * Inner class ValuesIterator.
     */
    protected static class ValuesIterator extends AbstractIteratorDecorator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
        /** The last returned value */
        protected Object lastValue = null;
        /** Whether remove is allowed at present */
        protected boolean canRemove = false;
         
        /**
         * Constructor.
         * @param iterator  the iterator to decorate
         * @param parent  the parent map
         */
        protected ValuesIterator(Iterator iterator, AbstractDualBidiMap parent) {
            super(iterator);
            this.parent = parent;
        }
         
        public Object next() {
            lastValue = super.next();
            canRemove = true;
            return lastValue;
        }
         
        public void remove() {
            if (canRemove == false) {
                throw new IllegalStateException("Iterator remove() can only be called once after next()");
            }
            super.remove(); // removes from maps[0]
            parent.maps[1].remove(lastValue);
            lastValue = null;
            canRemove = false;
        }
    }
 
    //-----------------------------------------------------------------------
    /**
     * Inner class EntrySet.
     */
    protected static class EntrySet extends View implements Set {
         
        /**
         * Constructs a new view of the BidiMap.
         *
         * @param parent  the parent BidiMap
         */
        protected EntrySet(AbstractDualBidiMap parent) {
            super(parent.maps[0].entrySet(), parent);
        }
 
        public Iterator iterator() {
            return parent.createEntrySetIterator(super.iterator());
        }
         
        public boolean remove(Object obj) {
            if (obj instanceof Map.Entry == false) {
                return false;
            }
            Map.Entry entry = (Map.Entry) obj;
            Object key = entry.getKey();
            if (parent.containsKey(key)) {
                Object value = parent.maps[0].get(key);
                if (value == null ? entry.getValue() == null : value.equals(entry.getValue())) {
                    parent.maps[0].remove(key);
                    parent.maps[1].remove(value);
                    return true;
                }
            }
            return false;
        }
    }
     
    /**
     * Inner class EntrySetIterator.
     */
    protected static class EntrySetIterator extends AbstractIteratorDecorator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
        /** The last returned entry */
        protected Map.Entry last = null;
        /** Whether remove is allowed at present */
        protected boolean canRemove = false;
         
        /**
         * Constructor.
         * @param iterator  the iterator to decorate
         * @param parent  the parent map
         */
        protected EntrySetIterator(Iterator iterator, AbstractDualBidiMap parent) {
            super(iterator);
            this.parent = parent;
        }
         
        public Object next() {
            last = new MapEntry((Map.Entry) super.next(), parent);
            canRemove = true;
            return last;
        }
         
        public void remove() {
            if (canRemove == false) {
                throw new IllegalStateException("Iterator remove() can only be called once after next()");
            }
            // store value as remove may change the entry in the decorator (eg.TreeMap)
            Object value = last.getValue();
            super.remove();
            parent.maps[1].remove(value);
            last = null;
            canRemove = false;
        }
    }
 
    /**
     * Inner class MapEntry.
     */
    protected static class MapEntry extends AbstractMapEntryDecorator {
 
        /** The parent map */       
        protected final AbstractDualBidiMap parent;
         
        /**
         * Constructor.
         * @param entry  the entry to decorate
         * @param parent  the parent map
         */
        protected MapEntry(Map.Entry entry, AbstractDualBidiMap parent) {
            super(entry);
            this.parent = parent;
        }
         
        public Object setValue(Object value) {
            Object key = MapEntry.this.getKey();
            if (parent.maps[1].containsKey(value) &&
                parent.maps[1].get(value) != key) {
                throw new IllegalArgumentException("Cannot use setValue() when the object being set is already in the map");
            }
            parent.put(key, value);
            final Object oldValue = super.setValue(value);
            return oldValue;
        }
    }
     
    /**
     * Inner class MapIterator.
     */
    protected static class BidiMapIterator implements MapIterator, ResettableIterator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
        /** The iterator being wrapped */
        protected Iterator iterator;
        /** The last returned entry */
        protected Map.Entry last = null;
        /** Whether remove is allowed at present */
        protected boolean canRemove = false;
         
        /**
         * Constructor.
         * @param parent  the parent map
         */
        protected BidiMapIterator(AbstractDualBidiMap parent) {
            super();
            this.parent = parent;
            this.iterator = parent.maps[0].entrySet().iterator();
        }
         
        public boolean hasNext() {
            return iterator.hasNext();
        }
         
        public Object next() {
            last = (Map.Entry) iterator.next();
            canRemove = true;
            return last.getKey();
        }
         
        public void remove() {
            if (canRemove == false) {
                throw new IllegalStateException("Iterator remove() can only be called once after next()");
            }
            // store value as remove may change the entry in the decorator (eg.TreeMap)
            Object value = last.getValue();
            iterator.remove();
            parent.maps[1].remove(value);
            last = null;
            canRemove = false;
        }
         
        public Object getKey() {
            if (last == null) {
                throw new IllegalStateException("Iterator getKey() can only be called after next() and before remove()");
            }
            return last.getKey();
        }
 
        public Object getValue() {
            if (last == null) {
                throw new IllegalStateException("Iterator getValue() can only be called after next() and before remove()");
            }
            return last.getValue();
        }
         
        public Object setValue(Object value) {
            if (last == null) {
                throw new IllegalStateException("Iterator setValue() can only be called after next() and before remove()");
            }
            if (parent.maps[1].containsKey(value) &&
                parent.maps[1].get(value) != last.getKey()) {
                throw new IllegalArgumentException("Cannot use setValue() when the object being set is already in the map");
            }
            return parent.put(last.getKey(), value);
        }
         
        public void reset() {
            iterator = parent.maps[0].entrySet().iterator();
            last = null;
            canRemove = false;
        }
         
        public String toString() {
            if (last != null) {
                return "MapIterator[" + getKey() + "=" + getValue() + "]";
            } else {
                return "MapIterator[]";
            }
        }
    }
     
}

 

 

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.commons.collections.bidimap;
 
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
 
import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.MapIterator;
import org.apache.commons.collections.ResettableIterator;
import org.apache.commons.collections.collection.AbstractCollectionDecorator;
import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
 
/**
 * Abstract <code>BidiMap</code> implemented using two maps.
 * <p>
 * An implementation can be written simply by implementing the
 * <code>createMap</code> method.
 *
 * @see DualHashBidiMap
 * @see DualTreeBidiMap
 * @since Commons Collections 3.0
 * @version $Id: AbstractDualBidiMap.java 646777 2008-04-10 12:33:15Z niallp $
 *
 * @author Matthew Hawthorne
 * @author Stephen Colebourne
 */
public abstract class AbstractDualBidiMap implements BidiMap {
 
    /**
     * Delegate map array.  The first map contains standard entries, and the
     * second contains inverses.
     */
    protected transient final Map[] maps = new Map[2];
    /**
     * Inverse view of this map.
     */
    protected transient BidiMap inverseBidiMap = null;
    /**
     * View of the keys.
     */
    protected transient Set keySet = null;
    /**
     * View of the values.
     */
    protected transient Collection values = null;
    /**
     * View of the entries.
     */
    protected transient Set entrySet = null;
 
    /**
     * Creates an empty map, initialised by <code>createMap</code>.
     * <p>
     * This constructor remains in place for deserialization.
     * All other usage is deprecated in favour of
     * {@link #AbstractDualBidiMap(Map, Map)}.
     */
    protected AbstractDualBidiMap() {
        super();
        maps[0] = createMap();
        maps[1] = createMap();
    }
 
    /**
     * Creates an empty map using the two maps specified as storage.
     * <p>
     * The two maps must be a matching pair, normal and reverse.
     * They will typically both be empty.
     * <p>
     * Neither map is validated, so nulls may be passed in.
     * If you choose to do this then the subclass constructor must populate
     * the <code>maps[]</code> instance variable itself.
     *
     * @param normalMap  the normal direction map
     * @param reverseMap  the reverse direction map
     * @since Commons Collections 3.1
     */
    protected AbstractDualBidiMap(Map normalMap, Map reverseMap) {
        super();
        maps[0] = normalMap;
        maps[1] = reverseMap;
    }
 
    /**
     * Constructs a map that decorates the specified maps,
     * used by the subclass <code>createBidiMap</code> implementation.
     *
     * @param normalMap  the normal direction map
     * @param reverseMap  the reverse direction map
     * @param inverseBidiMap  the inverse BidiMap
     */
    protected AbstractDualBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
        super();
        maps[0] = normalMap;
        maps[1] = reverseMap;
        this.inverseBidiMap = inverseBidiMap;
    }
 
    /**
     * Creates a new instance of the map used by the subclass to store data.
     * <p>
     * This design is deeply flawed and has been deprecated.
     * It relied on subclass data being used during a superclass constructor.
     *
     * @return the map to be used for internal storage
     * @deprecated For constructors, use the new two map constructor.
     * For deserialization, populate the maps array directly in readObject.
     */
    protected Map createMap() {
        return null;
    }
 
    /**
     * Creates a new instance of the subclass.
     *
     * @param normalMap  the normal direction map
     * @param reverseMap  the reverse direction map
     * @param inverseMap  this map, which is the inverse in the new map
     * @return the inverse map
     */
    protected abstract BidiMap createBidiMap(Map normalMap, Map reverseMap, BidiMap inverseMap);
 
    // Map delegation
    //-----------------------------------------------------------------------
    public Object get(Object key) {
        return maps[0].get(key);
    }
 
    public int size() {
        return maps[0].size();
    }
 
    public boolean isEmpty() {
        return maps[0].isEmpty();
    }
 
    public boolean containsKey(Object key) {
        return maps[0].containsKey(key);
    }
 
    public boolean equals(Object obj) {
        return maps[0].equals(obj);
    }
 
    public int hashCode() {
        return maps[0].hashCode();
    }
 
    public String toString() {
        return maps[0].toString();
    }
 
    // 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 Object remove(Object key) {
        Object value = null;
        if (maps[0].containsKey(key)) {
            value = maps[0].remove(key);
            maps[1].remove(value);
        }
        return value;
    }
 
    public void clear() {
        maps[0].clear();
        maps[1].clear();
    }
 
    public boolean containsValue(Object value) {
        return maps[1].containsKey(value);
    }
 
    // BidiMap
    //-----------------------------------------------------------------------
    /**
     * Obtains a <code>MapIterator</code> over the map.
     * The iterator implements <code>ResetableMapIterator</code>.
     * This implementation relies on the entrySet iterator.
     * <p>
     * The setValue() methods only allow a new value to be set.
     * If the value being set is already in the map, an IllegalArgumentException
     * is thrown (as setValue cannot change the size of the map).
     *
     * @return a map iterator
     */
    public MapIterator mapIterator() {
        return new BidiMapIterator(this);
    }
     
    public Object getKey(Object value) {
        return maps[1].get(value);
    }
 
    public Object removeValue(Object value) {
        Object key = null;
        if (maps[1].containsKey(value)) {
            key = maps[1].remove(value);
            maps[0].remove(key);
        }
        return key;
    }
 
    public BidiMap inverseBidiMap() {
        if (inverseBidiMap == null) {
            inverseBidiMap = createBidiMap(maps[1], maps[0], this);
        }
        return inverseBidiMap;
    }
     
    // Map views
    //-----------------------------------------------------------------------
    /**
     * Gets a keySet view of the map.
     * Changes made on the view are reflected in the map.
     * The set supports remove and clear but not add.
     *
     * @return the keySet view
     */
    public Set keySet() {
        if (keySet == null) {
            keySet = new KeySet(this);
        }
        return keySet;
    }
 
    /**
     * Creates a key set iterator.
     * Subclasses can override this to return iterators with different properties.
     *
     * @param iterator  the iterator to decorate
     * @return the keySet iterator
     */
    protected Iterator createKeySetIterator(Iterator iterator) {
        return new KeySetIterator(iterator, this);
    }
 
    /**
     * Gets a values view of the map.
     * Changes made on the view are reflected in the map.
     * The set supports remove and clear but not add.
     *
     * @return the values view
     */
    public Collection values() {
        if (values == null) {
            values = new Values(this);
        }
        return values;
    }
 
    /**
     * Creates a values iterator.
     * Subclasses can override this to return iterators with different properties.
     *
     * @param iterator  the iterator to decorate
     * @return the values iterator
     */
    protected Iterator createValuesIterator(Iterator iterator) {
        return new ValuesIterator(iterator, this);
    }
 
    /**
     * Gets an entrySet view of the map.
     * Changes made on the set are reflected in the map.
     * The set supports remove and clear but not add.
     * <p>
     * The Map Entry setValue() method only allow a new value to be set.
     * If the value being set is already in the map, an IllegalArgumentException
     * is thrown (as setValue cannot change the size of the map).
     *
     * @return the entrySet view
     */
    public Set entrySet() {
        if (entrySet == null) {
            entrySet = new EntrySet(this);
        }
        return entrySet;
    }
     
    /**
     * Creates an entry set iterator.
     * Subclasses can override this to return iterators with different properties.
     *
     * @param iterator  the iterator to decorate
     * @return the entrySet iterator
     */
    protected Iterator createEntrySetIterator(Iterator iterator) {
        return new EntrySetIterator(iterator, this);
    }
 
    //-----------------------------------------------------------------------
    /**
     * Inner class View.
     */
    protected static abstract class View extends AbstractCollectionDecorator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
         
        /**
         * Constructs a new view of the BidiMap.
         *
         * @param coll  the collection view being decorated
         * @param parent  the parent BidiMap
         */
        protected View(Collection coll, AbstractDualBidiMap parent) {
            super(coll);
            this.parent = parent;
        }
 
        public boolean removeAll(Collection coll) {
            if (parent.isEmpty() || coll.isEmpty()) {
                return false;
            }
            boolean modified = false;
            Iterator it = iterator();
            while (it.hasNext()) {
                if (coll.contains(it.next())) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }
 
        public boolean retainAll(Collection coll) {
            if (parent.isEmpty()) {
                return false;
            }
            if (coll.isEmpty()) {
                parent.clear();
                return true;
            }
            boolean modified = false;
            Iterator it = iterator();
            while (it.hasNext()) {
                if (coll.contains(it.next()) == false) {
                    it.remove();
                    modified = true;
                }
            }
            return modified;
        }
         
        public void clear() {
            parent.clear();
        }
    }
     
    //-----------------------------------------------------------------------
    /**
     * Inner class KeySet.
     */
    protected static class KeySet extends View implements Set {
         
        /**
         * Constructs a new view of the BidiMap.
         *
         * @param parent  the parent BidiMap
         */
        protected KeySet(AbstractDualBidiMap parent) {
            super(parent.maps[0].keySet(), parent);
        }
 
        public Iterator iterator() {
            return parent.createKeySetIterator(super.iterator());
        }
         
        public boolean contains(Object key) {
            return parent.maps[0].containsKey(key);
        }
 
        public boolean remove(Object key) {
            if (parent.maps[0].containsKey(key)) {
                Object value = parent.maps[0].remove(key);
                parent.maps[1].remove(value);
                return true;
            }
            return false;
        }
    }
     
    /**
     * Inner class KeySetIterator.
     */
    protected static class KeySetIterator extends AbstractIteratorDecorator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
        /** The last returned key */
        protected Object lastKey = null;
        /** Whether remove is allowed at present */
        protected boolean canRemove = false;
         
        /**
         * Constructor.
         * @param iterator  the iterator to decorate
         * @param parent  the parent map
         */
        protected KeySetIterator(Iterator iterator, AbstractDualBidiMap parent) {
            super(iterator);
            this.parent = parent;
        }
         
        public Object next() {
            lastKey = super.next();
            canRemove = true;
            return lastKey;
        }
         
        public void remove() {
            if (canRemove == false) {
                throw new IllegalStateException("Iterator remove() can only be called once after next()");
            }
            Object value = parent.maps[0].get(lastKey);
            super.remove();
            parent.maps[1].remove(value);
            lastKey = null;
            canRemove = false;
        }
    }
 
    //-----------------------------------------------------------------------
    /**
     * Inner class Values.
     */
    protected static class Values extends View implements Set {
         
        /**
         * Constructs a new view of the BidiMap.
         *
         * @param parent  the parent BidiMap
         */
        protected Values(AbstractDualBidiMap parent) {
            super(parent.maps[0].values(), parent);
        }
 
        public Iterator iterator() {
            return parent.createValuesIterator(super.iterator());
        }
         
        public boolean contains(Object value) {
            return parent.maps[1].containsKey(value);
        }
 
        public boolean remove(Object value) {
            if (parent.maps[1].containsKey(value)) {
                Object key = parent.maps[1].remove(value);
                parent.maps[0].remove(key);
                return true;
            }
            return false;
        }
    }
     
    /**
     * Inner class ValuesIterator.
     */
    protected static class ValuesIterator extends AbstractIteratorDecorator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
        /** The last returned value */
        protected Object lastValue = null;
        /** Whether remove is allowed at present */
        protected boolean canRemove = false;
         
        /**
         * Constructor.
         * @param iterator  the iterator to decorate
         * @param parent  the parent map
         */
        protected ValuesIterator(Iterator iterator, AbstractDualBidiMap parent) {
            super(iterator);
            this.parent = parent;
        }
         
        public Object next() {
            lastValue = super.next();
            canRemove = true;
            return lastValue;
        }
         
        public void remove() {
            if (canRemove == false) {
                throw new IllegalStateException("Iterator remove() can only be called once after next()");
            }
            super.remove(); // removes from maps[0]
            parent.maps[1].remove(lastValue);
            lastValue = null;
            canRemove = false;
        }
    }
 
    //-----------------------------------------------------------------------
    /**
     * Inner class EntrySet.
     */
    protected static class EntrySet extends View implements Set {
         
        /**
         * Constructs a new view of the BidiMap.
         *
         * @param parent  the parent BidiMap
         */
        protected EntrySet(AbstractDualBidiMap parent) {
            super(parent.maps[0].entrySet(), parent);
        }
 
        public Iterator iterator() {
            return parent.createEntrySetIterator(super.iterator());
        }
         
        public boolean remove(Object obj) {
            if (obj instanceof Map.Entry == false) {
                return false;
            }
            Map.Entry entry = (Map.Entry) obj;
            Object key = entry.getKey();
            if (parent.containsKey(key)) {
                Object value = parent.maps[0].get(key);
                if (value == null ? entry.getValue() == null : value.equals(entry.getValue())) {
                    parent.maps[0].remove(key);
                    parent.maps[1].remove(value);
                    return true;
                }
            }
            return false;
        }
    }
     
    /**
     * Inner class EntrySetIterator.
     */
    protected static class EntrySetIterator extends AbstractIteratorDecorator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
        /** The last returned entry */
        protected Map.Entry last = null;
        /** Whether remove is allowed at present */
        protected boolean canRemove = false;
         
        /**
         * Constructor.
         * @param iterator  the iterator to decorate
         * @param parent  the parent map
         */
        protected EntrySetIterator(Iterator iterator, AbstractDualBidiMap parent) {
            super(iterator);
            this.parent = parent;
        }
         
        public Object next() {
            last = new MapEntry((Map.Entry) super.next(), parent);
            canRemove = true;
            return last;
        }
         
        public void remove() {
            if (canRemove == false) {
                throw new IllegalStateException("Iterator remove() can only be called once after next()");
            }
            // store value as remove may change the entry in the decorator (eg.TreeMap)
            Object value = last.getValue();
            super.remove();
            parent.maps[1].remove(value);
            last = null;
            canRemove = false;
        }
    }
 
    /**
     * Inner class MapEntry.
     */
    protected static class MapEntry extends AbstractMapEntryDecorator {
 
        /** The parent map */       
        protected final AbstractDualBidiMap parent;
         
        /**
         * Constructor.
         * @param entry  the entry to decorate
         * @param parent  the parent map
         */
        protected MapEntry(Map.Entry entry, AbstractDualBidiMap parent) {
            super(entry);
            this.parent = parent;
        }
         
        public Object setValue(Object value) {
            Object key = MapEntry.this.getKey();
            if (parent.maps[1].containsKey(value) &&
                parent.maps[1].get(value) != key) {
                throw new IllegalArgumentException("Cannot use setValue() when the object being set is already in the map");
            }
            parent.put(key, value);
            final Object oldValue = super.setValue(value);
            return oldValue;
        }
    }
     
    /**
     * Inner class MapIterator.
     */
    protected static class BidiMapIterator implements MapIterator, ResettableIterator {
         
        /** The parent map */
        protected final AbstractDualBidiMap parent;
        /** The iterator being wrapped */
        protected Iterator iterator;
        /** The last returned entry */
        protected Map.Entry last = null;
        /** Whether remove is allowed at present */
        protected boolean canRemove = false;
         
        /**
         * Constructor.
         * @param parent  the parent map
         */
        protected BidiMapIterator(AbstractDualBidiMap parent) {
            super();
            this.parent = parent;
            this.iterator = parent.maps[0].entrySet().iterator();
        }
         
        public boolean hasNext() {
            return iterator.hasNext();
        }
         
        public Object next() {
            last = (Map.Entry) iterator.next();
            canRemove = true;
            return last.getKey();
        }
         
        public void remove() {
            if (canRemove == false) {
                throw new IllegalStateException("Iterator remove() can only be called once after next()");
            }
            // store value as remove may change the entry in the decorator (eg.TreeMap)
            Object value = last.getValue();
            iterator.remove();
            parent.maps[1].remove(value);
            last = null;
            canRemove = false;
        }
         
        public Object getKey() {
            if (last == null) {
                throw new IllegalStateException("Iterator getKey() can only be called after next() and before remove()");
            }
            return last.getKey();
        }
 
        public Object getValue() {
            if (last == null) {
                throw new IllegalStateException("Iterator getValue() can only be called after next() and before remove()");
            }
            return last.getValue();
        }
         
        public Object setValue(Object value) {
            if (last == null) {
                throw new IllegalStateException("Iterator setValue() can only be called after next() and before remove()");
            }
            if (parent.maps[1].containsKey(value) &&
                parent.maps[1].get(value) != last.getKey()) {
                throw new IllegalArgumentException("Cannot use setValue() when the object being set is already in the map");
            }
            return parent.put(last.getKey(), value);
        }
         
        public void reset() {
            iterator = parent.maps[0].entrySet().iterator();
            last = null;
            canRemove = false;
        }
         
        public String toString() {
            if (last != null) {
                return "MapIterator[" + getKey() + "=" + getValue() +"]";
            else {
                return "MapIterator[]";
            }
        }
    }
     
}

apache_commons 之 双向Map DualHashBidiMap (使用及源码)

标签:hash   hang   size   tin   创建   reflect   ati   which   获取   

原文地址:http://www.cnblogs.com/printN/p/6236390.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!