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:
Stephen Colebourne 2003-10-05 20:40:52 +00:00
parent 456adc3354
commit a322e63163
1 changed files with 310 additions and 177 deletions

View File

@ -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