Refactor implementation to act as more of a decorator
Fixed some bugs too git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131237 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
456adc3354
commit
a322e63163
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/Attic/HashBidiMap.java,v 1.3 2003/09/29 23:24:18 matth Exp $
|
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/Attic/HashBidiMap.java,v 1.4 2003/10/05 20:40:52 scolebourne Exp $
|
||||||
* ====================================================================
|
* ====================================================================
|
||||||
*
|
*
|
||||||
* The Apache Software License, Version 1.1
|
* The Apache Software License, Version 1.1
|
||||||
|
@ -57,236 +57,369 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections;
|
package org.apache.commons.collections;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.util.Collection;
|
||||||
import java.util.AbstractMap;
|
|
||||||
import java.util.AbstractSet;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.decorators.AbstractCollectionDecorator;
|
||||||
|
import org.apache.commons.collections.decorators.AbstractIteratorDecorator;
|
||||||
|
import org.apache.commons.collections.decorators.AbstractMapEntryDecorator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of <code>BidiMap</code>.
|
* Default implementation of <code>BidiMap</code>.
|
||||||
*
|
*
|
||||||
* @since Commons Collections 3.0
|
* @since Commons Collections 3.0
|
||||||
* @version $Id: HashBidiMap.java,v 1.3 2003/09/29 23:24:18 matth Exp $
|
* @version $Id: HashBidiMap.java,v 1.4 2003/10/05 20:40:52 scolebourne Exp $
|
||||||
*
|
*
|
||||||
* @author Matthew Hawthorne
|
* @author Matthew Hawthorne
|
||||||
*/
|
*/
|
||||||
public class HashBidiMap extends AbstractMap implements BidiMap, Serializable {
|
public class HashBidiMap implements BidiMap {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate map array. The first map contains standard entries, and the
|
* Delegate map array. The first map contains standard entries, and the
|
||||||
* second contains inverses.
|
* second contains inverses.
|
||||||
*/
|
*/
|
||||||
final Map[] maps = new Map[] { new HashMap(), new HashMap()};
|
protected final Map[] maps = new Map[2];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inverse view of this map.
|
* Inverse view of this map.
|
||||||
*/
|
*/
|
||||||
private final BidiMap inverseBidiMap = new InverseBidiMap();
|
protected BidiMap inverseBidiMap = null;
|
||||||
|
/**
|
||||||
|
* View of the keys.
|
||||||
|
*/
|
||||||
|
protected Set keySet = null;
|
||||||
|
/**
|
||||||
|
* View of the values.
|
||||||
|
*/
|
||||||
|
protected Collection values = null;
|
||||||
|
/**
|
||||||
|
* View of the entries.
|
||||||
|
*/
|
||||||
|
protected Set entrySet = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an empty <code>HashBidiMap</code>
|
* Creates an empty <code>HashBidiMap</code>
|
||||||
*/
|
*/
|
||||||
public HashBidiMap() {}
|
public HashBidiMap() {
|
||||||
|
super();
|
||||||
/**
|
maps[0] = new HashMap();
|
||||||
* Constructs a new <tt>HashMap</tt> with the same mappings as the
|
maps[1] = new HashMap();
|
||||||
* specified <tt>Map</tt>.
|
|
||||||
*
|
|
||||||
* @param m the map whose mappings are to be placed in this map.
|
|
||||||
*/
|
|
||||||
public HashBidiMap(Map m) {
|
|
||||||
putAll(m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 HashBidiMap(Map map) {
|
||||||
|
super();
|
||||||
|
maps[0] = new HashMap();
|
||||||
|
maps[1] = 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 HashBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
|
||||||
|
super();
|
||||||
|
maps[0] = normalMap;
|
||||||
|
maps[1] = reverseMap;
|
||||||
|
this.inverseBidiMap = inverseBidiMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
//-----------------------------------------------------------------------
|
||||||
public Object getKey(Object value) {
|
public Object getKey(Object value) {
|
||||||
return maps[1].get(value);
|
return maps[1].get(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object removeKey(Object value) {
|
||||||
|
Object key = null;
|
||||||
|
if (maps[1].containsKey(value)) {
|
||||||
|
key = maps[1].remove(value);
|
||||||
|
maps[0].remove(key);
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
public BidiMap inverseBidiMap() {
|
public BidiMap inverseBidiMap() {
|
||||||
|
if (inverseBidiMap == null) {
|
||||||
|
inverseBidiMap = new HashBidiMap(maps[1], maps[0], this);
|
||||||
|
}
|
||||||
return inverseBidiMap;
|
return inverseBidiMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object removeKey(Object value) {
|
// Map views
|
||||||
final Object key = maps[1].get(value);
|
//-----------------------------------------------------------------------
|
||||||
return remove(key);
|
public Set keySet() {
|
||||||
|
if (keySet == null) {
|
||||||
|
keySet = new KeySet(this);
|
||||||
|
}
|
||||||
|
return keySet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object put(Object key, Object value) {
|
public Collection values() {
|
||||||
// Removes pair from standard map if a previous inverse entry exists
|
if (values == null) {
|
||||||
final Object oldValue = maps[1].put(value, key);
|
values = new Values(this);
|
||||||
if (oldValue != null) {
|
|
||||||
maps[0].remove(oldValue);
|
|
||||||
}
|
}
|
||||||
|
return values;
|
||||||
final Object obj = maps[0].put(key, value);
|
|
||||||
return obj;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set entrySet() {
|
public Set entrySet() {
|
||||||
// The entrySet is the root of most Map methods, care must be taken not
|
if (entrySet == null) {
|
||||||
// to reference instance methods like size()
|
entrySet = new EntrySet(this);
|
||||||
|
}
|
||||||
|
return entrySet;
|
||||||
|
}
|
||||||
|
|
||||||
// Creates anonymous AbstractSet
|
//-----------------------------------------------------------------------
|
||||||
return new AbstractSet() {
|
/**
|
||||||
|
* Inner class View.
|
||||||
|
*/
|
||||||
|
protected static abstract class View extends AbstractCollectionDecorator {
|
||||||
|
|
||||||
|
protected final HashBidiMap map;
|
||||||
|
|
||||||
|
protected View(Collection coll, HashBidiMap map) {
|
||||||
|
super(coll);
|
||||||
|
this.map = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean removeAll(Collection coll) {
|
||||||
|
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) {
|
||||||
|
boolean modified = false;
|
||||||
|
Iterator it = iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
if (coll.contains(it.next()) == false) {
|
||||||
|
it.remove();
|
||||||
|
modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class KeySet.
|
||||||
|
*/
|
||||||
|
protected static class KeySet extends View implements Set {
|
||||||
|
|
||||||
|
protected KeySet(HashBidiMap map) {
|
||||||
|
super(map.maps[0].keySet(), map);
|
||||||
|
}
|
||||||
|
|
||||||
public Iterator iterator() {
|
public Iterator iterator() {
|
||||||
// Creates anonymous Iterator
|
return new AbstractIteratorDecorator(super.iterator()) {
|
||||||
return new Iterator() {
|
private Object last;
|
||||||
|
|
||||||
// Delegate iterator.
|
|
||||||
final Iterator it = maps[0].entrySet().iterator();
|
|
||||||
|
|
||||||
// Current iterator entry
|
|
||||||
Map.Entry currentEntry;
|
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
// Removes from standard and inverse Maps.
|
|
||||||
|
|
||||||
// Object must be removed using the iterator or a
|
|
||||||
// ConcurrentModificationException is thrown
|
|
||||||
it.remove();
|
|
||||||
HashBidiMap.this.maps[1].remove(
|
|
||||||
currentEntry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
return it.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object next() {
|
public Object next() {
|
||||||
currentEntry = (Map.Entry)it.next();
|
last = super.next();
|
||||||
|
return last;
|
||||||
// returns anonymous Map.Entry
|
|
||||||
return new Map.Entry() {
|
|
||||||
|
|
||||||
public Object getKey() {
|
|
||||||
return currentEntry.getKey();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getValue() {
|
public void remove() {
|
||||||
return currentEntry.getValue();
|
Object value = map.maps[0].get(last);
|
||||||
|
super.remove();
|
||||||
|
map.maps[1].remove(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(Object key) {
|
||||||
|
if (contains(key)) {
|
||||||
|
Object value = map.maps[0].remove(key);
|
||||||
|
map.maps[1].remove(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class Values.
|
||||||
|
*/
|
||||||
|
protected static class Values extends View {
|
||||||
|
|
||||||
|
protected Values(HashBidiMap map) {
|
||||||
|
super(map.maps[0].values(), map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator iterator() {
|
||||||
|
return new AbstractIteratorDecorator(super.iterator()) {
|
||||||
|
private Object last;
|
||||||
|
|
||||||
|
public Object next() {
|
||||||
|
last = super.next();
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
super.remove();
|
||||||
|
map.maps[1].remove(last);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean remove(Object value) {
|
||||||
|
if (contains(value)) {
|
||||||
|
Object key = map.maps[1].remove(value);
|
||||||
|
map.maps[0].remove(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner class EntrySet.
|
||||||
|
*/
|
||||||
|
protected static class EntrySet extends View implements Set {
|
||||||
|
|
||||||
|
protected EntrySet(HashBidiMap map) {
|
||||||
|
super(map.maps[0].entrySet(), map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator iterator() {
|
||||||
|
return new AbstractIteratorDecorator(super.iterator()) {
|
||||||
|
private Map.Entry last;
|
||||||
|
|
||||||
|
public Object next() {
|
||||||
|
last = new MapEntry((Map.Entry) super.next(), map);
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
super.remove();
|
||||||
|
map.maps[0].remove(last.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object setValue(Object value) {
|
|
||||||
final Object oldValue =
|
|
||||||
currentEntry.setValue(value);
|
|
||||||
|
|
||||||
// Gets old key and pairs with new value
|
|
||||||
final Object inverseKey =
|
|
||||||
HashBidiMap.this.maps[1].remove(oldValue);
|
|
||||||
HashBidiMap.this.maps[1].put(value, inverseKey);
|
|
||||||
|
|
||||||
return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // anonymous Map.Entry
|
|
||||||
};
|
};
|
||||||
}; // anonymous Iterator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean remove(Object obj) {
|
public boolean remove(Object obj) {
|
||||||
// XXX Throws ClassCastException if obj is not a Map.Entry.
|
if (obj instanceof Map.Entry == false) {
|
||||||
// Is this acceptable?
|
return false;
|
||||||
final Object removed =
|
}
|
||||||
HashBidiMap.this.remove(((Map.Entry)obj).getKey());
|
Map.Entry entry = (Map.Entry) obj;
|
||||||
return removed != null;
|
if (map.containsKey(entry.getKey())) {
|
||||||
|
Object value = map.maps[0].remove(entry.getKey());
|
||||||
|
map.maps[1].remove(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
protected static class MapEntry extends AbstractMapEntryDecorator {
|
||||||
return HashBidiMap.this.maps[0].size();
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // anonymous AbstractSet
|
protected final HashBidiMap map;
|
||||||
|
|
||||||
} // entrySet()
|
protected MapEntry(Map.Entry entry, HashBidiMap map) {
|
||||||
|
super(entry);
|
||||||
/**
|
this.map = map;
|
||||||
* Inverse view of this BidiMap.
|
|
||||||
*/
|
|
||||||
private final class InverseBidiMap extends AbstractMap implements BidiMap {
|
|
||||||
|
|
||||||
public Object getKey(Object value) {
|
|
||||||
return HashBidiMap.this.get(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BidiMap inverseBidiMap() {
|
|
||||||
return HashBidiMap.this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object removeKey(Object value) {
|
|
||||||
return HashBidiMap.this.remove(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set entrySet() {
|
|
||||||
// Gets entry set from outer class
|
|
||||||
final Set entrySet = HashBidiMap.this.entrySet();
|
|
||||||
|
|
||||||
// Returns anonymous Set
|
|
||||||
return new AbstractSet() {
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return HashBidiMap.this.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterator iterator() {
|
|
||||||
final Iterator delegate = entrySet.iterator();
|
|
||||||
|
|
||||||
// Returns anonymous Iterator
|
|
||||||
return new Iterator() {
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
return delegate.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object next() {
|
|
||||||
final Map.Entry entry = (Map.Entry)delegate.next();
|
|
||||||
|
|
||||||
// Returns anonymous Map.Entry
|
|
||||||
return new Map.Entry() {
|
|
||||||
|
|
||||||
public Object getKey() {
|
|
||||||
return entry.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getValue() {
|
|
||||||
return entry.getKey();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object setValue(Object value) {
|
public Object setValue(Object value) {
|
||||||
// This is confusing. Basically, we are
|
final Object oldValue = super.setValue(value);
|
||||||
// setting a new key for existing value
|
|
||||||
|
|
||||||
// Gets value for current key
|
// Gets old key and pairs with new value
|
||||||
final Object oldValue =
|
final Object inverseKey = map.maps[1].remove(oldValue);
|
||||||
HashBidiMap.this.maps[0].remove(getValue());
|
map.maps[1].put(value, inverseKey);
|
||||||
|
|
||||||
// Puts new key and value into map
|
|
||||||
HashBidiMap.this.maps[0].put(
|
|
||||||
value,
|
|
||||||
oldValue);
|
|
||||||
|
|
||||||
// Returns old value
|
|
||||||
return oldValue;
|
return oldValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // anonymous Map.Entry
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove() {
|
|
||||||
delegate.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // anonymous Iterator
|
|
||||||
}
|
|
||||||
|
|
||||||
}; // anonymous AbstractSet
|
|
||||||
|
|
||||||
} // entrySet()
|
|
||||||
|
|
||||||
} // InverseBidiMap
|
|
||||||
|
|
||||||
} // HashBidiMap
|
|
||||||
|
|
Loading…
Reference in New Issue