Rename HashBidiMap to DualHashBidiMap

Add AbstractDualBidiMap
Test and fix bugs


git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131254 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2003-10-06 23:47:17 +00:00
parent 8755bd9684
commit b943acfc64
6 changed files with 268 additions and 127 deletions

View File

@ -1,10 +1,10 @@
/*
* $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 $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/Attic/AbstractDualBidiMap.java,v 1.1 2003/10/06 23:47:17 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2002-2003 The Apache Software Foundation. All rights
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -58,7 +58,6 @@
package org.apache.commons.collections;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@ -68,72 +67,75 @@ import org.apache.commons.collections.decorators.AbstractIteratorDecorator;
import org.apache.commons.collections.decorators.AbstractMapEntryDecorator;
/**
* Default implementation of <code>BidiMap</code>.
* Abstract <code>BidiMap</code> implemented using two maps.
* <p>
* An implementation can be written simply by implementing the
* <code>createMap</code> method.
*
* @since Commons Collections 3.0
* @version $Id: HashBidiMap.java,v 1.4 2003/10/05 20:40:52 scolebourne Exp $
* @version $Id: AbstractDualBidiMap.java,v 1.1 2003/10/06 23:47:17 scolebourne Exp $
*
* @author Matthew Hawthorne
* @author Stephen Colebourne
*/
public class HashBidiMap implements BidiMap {
public abstract class AbstractDualBidiMap implements BidiMap {
/**
* Delegate map array. The first map contains standard entries, and the
* second contains inverses.
*/
protected final Map[] maps = new Map[2];
protected transient final Map[] maps = new Map[2];
/**
* Inverse view of this map.
*/
protected BidiMap inverseBidiMap = null;
protected transient BidiMap inverseBidiMap = null;
/**
* View of the keys.
*/
protected Set keySet = null;
/**
* View of the values.
*/
protected Collection values = null;
protected transient Set keySet = null;
/**
* View of the entries.
*/
protected Set entrySet = null;
protected transient Set entrySet = null;
/**
* Creates an empty <code>HashBidiMap</code>
* Creates an empty map.
* <p>
* The maps passed in are not validated, so subclasses need to ensure
* that they are non-null, empty and compatible.
*
* @param normalMap the normal direction map
* @param reverseMap the reverse direction map
*/
public HashBidiMap() {
protected AbstractDualBidiMap(Map normalMap, Map reverseMap) {
super();
maps[0] = new HashMap();
maps[1] = new HashMap();
maps[0] = normalMap;
maps[1] = reverseMap;
}
/**
* 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.
* Constructs a map 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) {
protected AbstractDualBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
super();
maps[0] = normalMap;
maps[1] = reverseMap;
this.inverseBidiMap = inverseBidiMap;
}
/**
* 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
//-----------------------------------------------------------------------
@ -221,11 +223,11 @@ public class HashBidiMap implements BidiMap {
public BidiMap inverseBidiMap() {
if (inverseBidiMap == null) {
inverseBidiMap = new HashBidiMap(maps[1], maps[0], this);
inverseBidiMap = createBidiMap(maps[1], maps[0], this);
}
return inverseBidiMap;
}
// Map views
//-----------------------------------------------------------------------
public Set keySet() {
@ -236,10 +238,7 @@ public class HashBidiMap implements BidiMap {
}
public Collection values() {
if (values == null) {
values = new Values(this);
}
return values;
return inverseBidiMap().keySet();
}
public Set entrySet() {
@ -255,14 +254,17 @@ public class HashBidiMap implements BidiMap {
*/
protected static abstract class View extends AbstractCollectionDecorator {
protected final HashBidiMap map;
protected final AbstractDualBidiMap map;
protected View(Collection coll, HashBidiMap map) {
protected View(Collection coll, AbstractDualBidiMap map) {
super(coll);
this.map = map;
}
public boolean removeAll(Collection coll) {
if (map.isEmpty() || coll.isEmpty()) {
return false;
}
boolean modified = false;
Iterator it = iterator();
while (it.hasNext()) {
@ -275,6 +277,13 @@ public class HashBidiMap implements BidiMap {
}
public boolean retainAll(Collection coll) {
if (map.isEmpty()) {
return false;
}
if (coll.isEmpty()) {
map.clear();
return true;
}
boolean modified = false;
Iterator it = iterator();
while (it.hasNext()) {
@ -296,25 +305,12 @@ public class HashBidiMap implements BidiMap {
*/
protected static class KeySet extends View implements Set {
protected KeySet(HashBidiMap map) {
protected KeySet(AbstractDualBidiMap map) {
super(map.maps[0].keySet(), map);
}
public Iterator iterator() {
return new AbstractIteratorDecorator(super.iterator()) {
private Object last;
public Object next() {
last = super.next();
return last;
}
public void remove() {
Object value = map.maps[0].get(last);
super.remove();
map.maps[1].remove(value);
}
};
return new KeySetIterator(super.iterator(), map);
}
public boolean remove(Object key) {
@ -328,64 +324,42 @@ public class HashBidiMap implements BidiMap {
}
/**
* Inner class Values.
* Inner class KeySetIterator.
*/
protected static class Values extends View {
protected static class KeySetIterator extends AbstractIteratorDecorator {
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);
}
};
private final AbstractDualBidiMap map;
private Object last;
protected KeySetIterator(Iterator iterator, AbstractDualBidiMap map) {
super(iterator);
this.map = map;
}
public boolean remove(Object value) {
if (contains(value)) {
Object key = map.maps[1].remove(value);
map.maps[0].remove(key);
return true;
}
return false;
public Object next() {
last = super.next();
return last;
}
public void remove() {
Object value = map.maps[0].get(last);
super.remove();
map.maps[1].remove(value);
last = null;
}
}
/**
* Inner class EntrySet.
*/
protected static class EntrySet extends View implements Set {
protected EntrySet(HashBidiMap map) {
protected EntrySet(AbstractDualBidiMap 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());
}
};
return new EntrySetIterator(super.iterator(), map);
}
public boolean remove(Object obj) {
@ -402,11 +376,39 @@ public class HashBidiMap implements BidiMap {
}
}
/**
* Inner class EntrySetIterator.
*/
protected static class EntrySetIterator extends AbstractIteratorDecorator {
private final AbstractDualBidiMap map;
private Map.Entry last;
protected EntrySetIterator(Iterator iterator, AbstractDualBidiMap map) {
super(iterator);
this.map = map;
}
public Object next() {
last = new MapEntry((Map.Entry) super.next(), map);
return last;
}
public void remove() {
super.remove();
map.maps[1].remove(last.getValue());
last = null;
}
}
/**
* Inner class MapEntry.
*/
protected static class MapEntry extends AbstractMapEntryDecorator {
protected final HashBidiMap map;
protected final AbstractDualBidiMap map;
protected MapEntry(Map.Entry entry, HashBidiMap map) {
protected MapEntry(Map.Entry entry, AbstractDualBidiMap map) {
super(entry);
this.map = map;
}

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/BidiMap.java,v 1.2 2003/10/05 20:38:55 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/BidiMap.java,v 1.3 2003/10/06 23:47:17 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -62,11 +62,20 @@ import java.util.Map;
/**
* Defines a map that allows bidirectional lookup between key and values.
* <p>
* This extended <code>Map</code> represents a mapping where a key may
* lookup a value and a value may lookup a key with equal ease.
* Th interface extends <code>Map</code> and so may be used anywhere a map
* is required. The interface provides an inverse map view, enabling
* full access to both directions of the <code>BidiMap</code>.
* <p>
* Implementations should allow a value to be looked up from a key and
* a key to be looked up from a value with equal performance.
*
* It should be noted that the quickest way to implement the <code>values</code>
* method is usually to return <code>inverseBidiMap().keySet()</code>.
*
* @see org.apache.commons.collections.DualHashBidiMap
* @since Commons Collections 3.0
* @version $Revision: 1.2 $ $Date: 2003/10/05 20:38:55 $
* @version $Revision: 1.3 $ $Date: 2003/10/06 23:47:17 $
*
* @author Stephen Colebourne
*/
@ -79,11 +88,11 @@ public interface BidiMap extends Map {
* against a different key. That mapping is removed, to ensure that the
* value only occurs once in the inverse map.
* <pre>
* BidiMap map1 = new HashBidiMap();
* BidiMap map1 = new DualHashBidiMap();
* map.put("A","B"); // contains A mapped to B, as per Map
* map.put("A","C"); // contains A mapped to C, as per Map
*
* BidiMap map2 = new HashBidiMap();
* BidiMap map2 = new DualHashBidiMap();
* map.put("A","B"); // contains A mapped to B, as per Map
* map.put("C","B"); // contains C mapped to B, key A is removed
* </pre>
@ -100,7 +109,7 @@ public interface BidiMap extends Map {
* @throws NullPointerException (optional) if the map limits the values to
* non-null and null was specified
*/
public Object put(Object key, Object value);
Object put(Object key, Object value);
/**
* Gets the key that is currently mapped to the specified value.
@ -118,7 +127,7 @@ public interface BidiMap extends Map {
* @throws NullPointerException (optional) if the map limits the values to
* non-null and null was specified
*/
public Object getKey(Object value);
Object getKey(Object value);
/**
* Removes the key-value pair that is currently mapped to the specified
@ -139,7 +148,7 @@ public interface BidiMap extends Map {
* @throws UnsupportedOperationException if this method is not supported
* by the implementation
*/
public Object removeKey(Object value);
Object removeKey(Object value);
/**
* Gets a view of this map where the keys and values are reversed.
@ -153,6 +162,6 @@ public interface BidiMap extends Map {
*
* @return an inverted bidirectional map
*/
public BidiMap inverseBidiMap();
BidiMap inverseBidiMap();
}

View File

@ -0,0 +1,116 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/Attic/DualHashBidiMap.java,v 1.1 2003/10/06 23:47:17 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgement may appear in the software itself,
* if and wherever such third-party acknowledgements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.collections;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of <code>BidiMap</code> that uses two <code>HashMap</code> instances.
*
* @since Commons Collections 3.0
* @version $Id: DualHashBidiMap.java,v 1.1 2003/10/06 23:47:17 scolebourne Exp $
*
* @author Matthew Hawthorne
* @author Stephen Colebourne
*/
public class DualHashBidiMap extends AbstractDualBidiMap {
/**
* 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 inverseMap) {
return new DualHashBidiMap(normalMap, reverseMap, inverseMap);
}
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/TestAll.java,v 1.49 2003/10/05 21:03:44 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/TestAll.java,v 1.50 2003/10/06 23:47:17 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -64,7 +64,7 @@ import junit.framework.TestSuite;
/**
* Entry point for all Collections package tests.
*
* @version $Revision: 1.49 $ $Date: 2003/10/05 21:03:44 $
* @version $Revision: 1.50 $ $Date: 2003/10/06 23:47:17 $
*
* @author Rodney Waldhoff
* @author Stephen Colebourne
@ -104,7 +104,7 @@ public class TestAll extends TestCase {
suite.addTest(TestFastTreeMap.suite());
suite.addTest(TestFastTreeMap1.suite());
suite.addTest(TestHashBag.suite());
suite.addTest(TestHashBidiMap.suite());
suite.addTest(TestDualHashBidiMap.suite());
suite.addTest(TestIteratorUtils.suite());
suite.addTest(TestLRUMap.suite());
suite.addTest(TestMultiHashMap.suite());

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/Attic/TestBidiMap.java,v 1.4 2003/10/05 20:52:29 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/Attic/TestBidiMap.java,v 1.5 2003/10/06 23:47:17 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -63,7 +63,7 @@ import java.util.Map;
/**
* JUnit tests.
*
* @version $Revision: 1.4 $ $Date: 2003/10/05 20:52:29 $
* @version $Revision: 1.5 $ $Date: 2003/10/06 23:47:17 $
*
* @author Matthew Hawthorne
*/
@ -132,13 +132,6 @@ public abstract class TestBidiMap extends AbstractTestMap {
return false;
}
/**
* Override to prevent infinite recursion of tests.
*/
protected String[] ignoredTests() {
return new String[] {"TestHashBidiMap.bulkTestInverseMap.bulkTestInverseMap"};
}
// BidiPut
//-----------------------------------------------------------------------
public void testBidiPut() {
@ -172,6 +165,19 @@ public abstract class TestBidiMap extends AbstractTestMap {
assertEquals("E", inverse.get("F"));
}
/**
* Verifies that {@link #map} is still equal to {@link #confirmed}.
* <p>
* This implementation checks the inverse map as well.
*/
protected void verify() {
// verify inverse
assertEquals(map.size(), ((BidiMap) map).inverseBidiMap().size());
// verify fully
super.verify();
}
// testGetKey
//-----------------------------------------------------------------------
public void testBidiGetKey() {

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/Attic/TestHashBidiMap.java,v 1.2 2003/10/05 20:52:29 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/Attic/TestDualHashBidiMap.java,v 1.1 2003/10/06 23:47:17 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -63,26 +63,34 @@ import junit.textui.TestRunner;
/**
* JUnit tests.
*
* @version $Revision: 1.2 $ $Date: 2003/10/05 20:52:29 $
* @version $Revision: 1.1 $ $Date: 2003/10/06 23:47:17 $
*
* @author Matthew Hawthorne
* @author Stephen Colebourne
*/
public class TestHashBidiMap extends TestBidiMap {
public class TestDualHashBidiMap extends TestBidiMap {
public static void main(String[] args) {
TestRunner.run(suite());
}
public static Test suite() {
return BulkTest.makeSuite(TestHashBidiMap.class);
return BulkTest.makeSuite(TestDualHashBidiMap.class);
}
public TestHashBidiMap(String testName) {
public TestDualHashBidiMap(String testName) {
super(testName);
}
protected BidiMap makeEmptyBidiMap() {
return new HashBidiMap();
return new DualHashBidiMap();
}
/**
* Override to prevent infinite recursion of tests.
*/
protected String[] ignoredTests() {
return new String[] {"TestDualHashBidiMap.bulkTestInverseMap.bulkTestInverseMap"};
}
}