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[]"; } } } }apache_commons 之 双向Map DualHashBidiMap (使用及源码)的更多相关文章
- MapReduce中map并行度优化及源码分析
mapTask并行度的决定机制 一个job的map阶段并行度由客户端在提交job时决定,而客户端对map阶段并行度的规划的基本逻辑为:将待处理数据执行逻辑切片(即按照一个特定切片大小,将待处理数据划分 ...
- Map集合类(一.hashMap源码解析jdk1.8)
java集合笔记一 java集合笔记二 java集合笔记三 jdk 8 之前,其内部是由数组+链表来实现的,而 jdk 8 对于链表长度超过 8 的链表将转储为红黑树 1.属性 //节点数组,第一次使 ...
- STL源码中map和set中key值不能修改的实现
前言 最近正好刚刚看完,<stl源码剖析>这本书的map和set的源码部分.但是看完之后又突然发现,之前怎么没有注意到map和set容器中key不能修改是怎么实现的.故,特此整理如下. s ...
- [源码解析]为什么mapPartition比map更高效
[源码解析]为什么mapPartition比map更高效 目录 [源码解析]为什么mapPartition比map更高效 0x00 摘要 0x01 map vs mapPartition 1.1 ma ...
- HashMap源码:聊聊Map的遍历性能问题(一)
目录 引言 迭代器测试 迭代器源码探究 其他遍历方法 增强型for循环 Map.forEach Stream.forEach 总结 附:四种遍历源码 附:完整测试类与测试结果+一个奇怪的问题 引言 今 ...
- Java 容器源码分析之Map-Set-List
HashMap 的实现原理 HashMap 概述 HashMap 是基于哈希表的 Map 接口的非同步实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.此类不保证映射的顺序 ...
- vue数据绑定源码
思路分析 数据的双向绑定,就是数据变化了自动更新视图,视图变化了自动更新数据,实际上视图变化更新数据只要通过事件监听就可以实现了,并不是数据双向绑定的关键点.关键还是数据变化了驱动视图自动更新. 所有 ...
- 【nodejs原理&源码杂记(8)】Timer模块与基于二叉堆的定时器
[摘要] timers模块部分源码和定时器原理 示例代码托管在:http://www.github.com/dashnowords/blogs 一.概述 Timer模块相关的逻辑较为复杂,不仅包含Ja ...
- 【nodejs原理&源码杂记(8)】Timer模块与基于二叉堆的定时器
目录 一.概述 二. 数据结构 2.1 链表 2.2 二叉堆 三. 从setTimeout理解Timer模块源码 3.1 timers.js中的定义 3.2 Timeout类定义 3.3 active ...
随机推荐
- 关于case语句中声明变量并初始化的注意事项
今天看到一句对这个问题特别精辟的总结,记录如下: It is possible to transfer into a block, but not in a way that bypasses dec ...
- JS控制键盘录入 和 window.event.keycode对照
一.只允许录入整数 1.不允许录入非数字(按下字母键就会提示并清空) function intOnly() { if (!(window.event.keyCode >= 48 &&am ...
- Oracle基础——学习笔记
一[用户]sys\system\sysman\scott 1.查看数据库所有用户(dba_users数据字典): select username from dba_users; 2.查看当前用户: s ...
- java正则表达式
java正则表达式 1.Java正则表达式的语法与示例: http://baike.xsoftlab.net/view/207.html 2.Java 正则表达式: http://www.runo ...
- POJ 3370. Halloween treats 抽屉原理 / 鸽巢原理
Halloween treats Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 7644 Accepted: 2798 ...
- Linux多线程学习总结
线程是程序中完成一个独立任务的完整执行序列,即一个可调度的实体:进程相当于运行中程序的一种抽象.根据运行环境的调度者的身份,线程可分为内核线程和用户线程.内核线程,在有的系统上称为LWP(Light ...
- Spring AspectJ基于注解的AOP实现
对于AOP这种编程思想,很多框架都进行了实现.Spring就是其中之一,可以完成面向切面编程.然而,AspectJ也实现了AOP的功能,且实现方式更为简捷,使用更加方便,而且还支持注解式开发.所以,S ...
- [No00009B]win10快捷键大全
微软为Win10命令行(Command Prompt)加入了Ctrl + V的支持 Win10新增功能快捷键大全: 贴靠窗口:Win + 左/右 > Win + 上/下 > 窗口可以变为 ...
- HFSS仿真小例
微带拐角对比实验 微带四分之波长变换器插入前后对比 P1 P2 分配波端口激励,设置差分信号线 微带线差分对的差模阻抗随着差分线间距的增大而增大,随着线宽的增大而减小: 共模阻抗随着差分线间距的增大而 ...
- LINUX操作系统VIM的安装和配置
VIM的安装 在命令行敲入"vi"后按"tab"键,可以看到目前系统中只安装了vi和vim.tiny. vim是从VI发展而来的一个文本编辑器,功能更强大. ...