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

在项目当中,经常出现需要根据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[]";            }        }    }     }
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号