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
|
||||
|
@ -57,236 +57,369 @@
|
|||
*/
|
||||
package org.apache.commons.collections;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.collections.decorators.AbstractCollectionDecorator;
|
||||
import org.apache.commons.collections.decorators.AbstractIteratorDecorator;
|
||||
import org.apache.commons.collections.decorators.AbstractMapEntryDecorator;
|
||||
|
||||
/**
|
||||
* Default implementation of <code>BidiMap</code>.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public class HashBidiMap extends AbstractMap implements BidiMap, Serializable {
|
||||
public class HashBidiMap implements BidiMap {
|
||||
|
||||
/**
|
||||
* Delegate map array. The first map contains standard entries, and the
|
||||
* second contains inverses.
|
||||
*/
|
||||
final Map[] maps = new Map[] { new HashMap(), new HashMap()};
|
||||
|
||||
protected final Map[] maps = new Map[2];
|
||||
/**
|
||||
* Inverse view of this map.
|
||||
*/
|
||||
private final BidiMap inverseBidiMap = new InverseBidiMap();
|
||||
protected BidiMap inverseBidiMap = null;
|
||||
/**
|
||||
* View of the keys.
|
||||
*/
|
||||
protected Set keySet = null;
|
||||
/**
|
||||
* View of the values.
|
||||
*/
|
||||
protected Collection values = null;
|
||||
/**
|
||||
* View of the entries.
|
||||
*/
|
||||
protected Set entrySet = null;
|
||||
|
||||
/**
|
||||
* Creates an empty <code>HashBidiMap</code>
|
||||
*/
|
||||
public HashBidiMap() {}
|
||||
|
||||
/**
|
||||
* Constructs a new <tt>HashMap</tt> with the same mappings as the
|
||||
* specified <tt>Map</tt>.
|
||||
*
|
||||
* @param m the map whose mappings are to be placed in this map.
|
||||
*/
|
||||
public HashBidiMap(Map m) {
|
||||
putAll(m);
|
||||
public HashBidiMap() {
|
||||
super();
|
||||
maps[0] = new HashMap();
|
||||
maps[1] = 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 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) {
|
||||
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() {
|
||||
if (inverseBidiMap == null) {
|
||||
inverseBidiMap = new HashBidiMap(maps[1], maps[0], this);
|
||||
}
|
||||
return inverseBidiMap;
|
||||
}
|
||||
|
||||
public Object removeKey(Object value) {
|
||||
final Object key = maps[1].get(value);
|
||||
return remove(key);
|
||||
// Map views
|
||||
//-----------------------------------------------------------------------
|
||||
public Set keySet() {
|
||||
if (keySet == null) {
|
||||
keySet = new KeySet(this);
|
||||
}
|
||||
return keySet;
|
||||
}
|
||||
|
||||
public Object put(Object key, Object value) {
|
||||
// Removes pair from standard map if a previous inverse entry exists
|
||||
final Object oldValue = maps[1].put(value, key);
|
||||
if (oldValue != null) {
|
||||
maps[0].remove(oldValue);
|
||||
public Collection values() {
|
||||
if (values == null) {
|
||||
values = new Values(this);
|
||||
}
|
||||
|
||||
final Object obj = maps[0].put(key, value);
|
||||
return obj;
|
||||
return values;
|
||||
}
|
||||
|
||||
public Set entrySet() {
|
||||
// The entrySet is the root of most Map methods, care must be taken not
|
||||
// to reference instance methods like size()
|
||||
|
||||
// Creates anonymous AbstractSet
|
||||
return new AbstractSet() {
|
||||
|
||||
public Iterator iterator() {
|
||||
// Creates anonymous Iterator
|
||||
return new Iterator() {
|
||||
|
||||
// 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() {
|
||||
currentEntry = (Map.Entry)it.next();
|
||||
|
||||
// returns anonymous Map.Entry
|
||||
return new Map.Entry() {
|
||||
|
||||
public Object getKey() {
|
||||
return currentEntry.getKey();
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return currentEntry.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) {
|
||||
// XXX Throws ClassCastException if obj is not a Map.Entry.
|
||||
// Is this acceptable?
|
||||
final Object removed =
|
||||
HashBidiMap.this.remove(((Map.Entry)obj).getKey());
|
||||
return removed != null;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return HashBidiMap.this.maps[0].size();
|
||||
}
|
||||
|
||||
}; // anonymous AbstractSet
|
||||
|
||||
} // entrySet()
|
||||
|
||||
if (entrySet == null) {
|
||||
entrySet = new EntrySet(this);
|
||||
}
|
||||
return entrySet;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Inverse view of this BidiMap.
|
||||
* Inner class View.
|
||||
*/
|
||||
private final class InverseBidiMap extends AbstractMap implements BidiMap {
|
||||
|
||||
public Object getKey(Object value) {
|
||||
return HashBidiMap.this.get(value);
|
||||
protected static abstract class View extends AbstractCollectionDecorator {
|
||||
|
||||
protected final HashBidiMap map;
|
||||
|
||||
protected View(Collection coll, HashBidiMap map) {
|
||||
super(coll);
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public BidiMap inverseBidiMap() {
|
||||
return HashBidiMap.this;
|
||||
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 Object removeKey(Object value) {
|
||||
return HashBidiMap.this.remove(value);
|
||||
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 Set entrySet() {
|
||||
// Gets entry set from outer class
|
||||
final Set entrySet = HashBidiMap.this.entrySet();
|
||||
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);
|
||||
}
|
||||
|
||||
// Returns anonymous Set
|
||||
return new AbstractSet() {
|
||||
|
||||
public int size() {
|
||||
return HashBidiMap.this.size();
|
||||
public Iterator iterator() {
|
||||
return new AbstractIteratorDecorator(super.iterator()) {
|
||||
private Object last;
|
||||
|
||||
public Object next() {
|
||||
last = super.next();
|
||||
return last;
|
||||
}
|
||||
|
||||
public Iterator iterator() {
|
||||
final Iterator delegate = entrySet.iterator();
|
||||
public void remove() {
|
||||
Object value = map.maps[0].get(last);
|
||||
super.remove();
|
||||
map.maps[1].remove(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean remove(Object key) {
|
||||
if (contains(key)) {
|
||||
Object value = map.maps[0].remove(key);
|
||||
map.maps[1].remove(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class Values.
|
||||
*/
|
||||
protected static class Values extends View {
|
||||
|
||||
protected Values(HashBidiMap map) {
|
||||
super(map.maps[0].values(), map);
|
||||
}
|
||||
|
||||
// Returns anonymous Iterator
|
||||
return new Iterator() {
|
||||
|
||||
public boolean hasNext() {
|
||||
return delegate.hasNext();
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
final Map.Entry entry = (Map.Entry)delegate.next();
|
||||
|
||||
// Returns anonymous Map.Entry
|
||||
return new Map.Entry() {
|
||||
|
||||
public Object getKey() {
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
public Object setValue(Object value) {
|
||||
// This is confusing. Basically, we are
|
||||
// setting a new key for existing value
|
||||
|
||||
// Gets value for current key
|
||||
final Object oldValue =
|
||||
HashBidiMap.this.maps[0].remove(getValue());
|
||||
|
||||
// Puts new key and value into map
|
||||
HashBidiMap.this.maps[0].put(
|
||||
value,
|
||||
oldValue);
|
||||
|
||||
// Returns old value
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
}; // anonymous Map.Entry
|
||||
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
delegate.remove();
|
||||
}
|
||||
|
||||
}; // anonymous Iterator
|
||||
public Iterator iterator() {
|
||||
return new AbstractIteratorDecorator(super.iterator()) {
|
||||
private Object last;
|
||||
|
||||
public Object next() {
|
||||
last = super.next();
|
||||
return last;
|
||||
}
|
||||
|
||||
}; // anonymous AbstractSet
|
||||
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);
|
||||
}
|
||||
|
||||
} // entrySet()
|
||||
public Iterator iterator() {
|
||||
return new AbstractIteratorDecorator(super.iterator()) {
|
||||
private Map.Entry last;
|
||||
|
||||
public Object next() {
|
||||
last = new MapEntry((Map.Entry) super.next(), map);
|
||||
return last;
|
||||
}
|
||||
|
||||
} // InverseBidiMap
|
||||
public void remove() {
|
||||
super.remove();
|
||||
map.maps[0].remove(last.getValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean remove(Object obj) {
|
||||
if (obj instanceof Map.Entry == false) {
|
||||
return false;
|
||||
}
|
||||
Map.Entry entry = (Map.Entry) obj;
|
||||
if (map.containsKey(entry.getKey())) {
|
||||
Object value = map.maps[0].remove(entry.getKey());
|
||||
map.maps[1].remove(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected static class MapEntry extends AbstractMapEntryDecorator {
|
||||
|
||||
protected final HashBidiMap map;
|
||||
|
||||
protected MapEntry(Map.Entry entry, HashBidiMap map) {
|
||||
super(entry);
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public Object setValue(Object value) {
|
||||
final Object oldValue = super.setValue(value);
|
||||
|
||||
} // HashBidiMap
|
||||
// Gets old key and pairs with new value
|
||||
final Object inverseKey = map.maps[1].remove(oldValue);
|
||||
map.maps[1].put(value, inverseKey);
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue