You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by tn...@apache.org on 2013/03/02 13:54:48 UTC
svn commit: r1451883 - in /commons/proper/collections/trunk/src:
changes/changes.xml
main/java/org/apache/commons/collections/collection/IndexedCollection.java
test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java
Author: tn
Date: Sat Mar 2 12:54:47 2013
New Revision: 1451883
URL: http://svn.apache.org/r1451883
Log:
[COLLECTIONS-275] Finished IndexedCollection by supporting non-uniqueness of keys, fixes tests.
Modified:
commons/proper/collections/trunk/src/changes/changes.xml
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java
Modified: commons/proper/collections/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/changes/changes.xml?rev=1451883&r1=1451882&r2=1451883&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/changes/changes.xml (original)
+++ commons/proper/collections/trunk/src/changes/changes.xml Sat Mar 2 12:54:47 2013
@@ -22,6 +22,10 @@
<body>
<release version="4.0" date="TBA" description="Next release">
+ <action issue="COLLECTIONS-275" dev="tn" type="add" due-to="Stephen Kestle">
+ Added "IndexedCollection" collection decorator which provides a map-like
+ view on an existing collection.
+ </action>
<action issue="COLLECTIONS-258" dev="tn" type="add" due-to="Nathan Blomquist">
Added "DualLinkedHashBidiMap" bidi map implementation.
</action>
Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java?rev=1451883&r1=1451882&r2=1451883&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java (original)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections/collection/IndexedCollection.java Sat Mar 2 12:54:47 2013
@@ -18,9 +18,10 @@ package org.apache.commons.collections.c
import java.util.Collection;
import java.util.HashMap;
-import java.util.Map;
+import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.Transformer;
+import org.apache.commons.collections.map.MultiValueMap;
/**
* An IndexedCollection is a Map-like view onto a Collection. It accepts a
@@ -40,7 +41,6 @@ import org.apache.commons.collections.Tr
* @since 4.0
* @version $Id$
*/
-// TODO support MultiMap/non-unique index behavior
public class IndexedCollection<K, C> extends AbstractCollectionDecorator<C> {
/** Serialization version */
@@ -50,10 +50,16 @@ public class IndexedCollection<K, C> ext
private final Transformer<C, K> keyTransformer;
/** The map of indexes to collected objects. */
- private final Map<K, C> index;
+ private final MultiMap<K, C> index;
+
+ /** The uniqueness constraint for the index. */
+ private final boolean uniqueIndex;
/**
* Create an {@link IndexedCollection} for a unique index.
+ * <p>
+ * If an element is added, which maps to an existing key, an {@link IllegalArgumentException}
+ * will be thrown.
*
* @param <K> the index object type.
* @param <C> the collection type.
@@ -63,24 +69,50 @@ public class IndexedCollection<K, C> ext
*/
public static <K, C> IndexedCollection<K, C> uniqueIndexedCollection(final Collection<C> coll,
final Transformer<C, K> keyTransformer) {
- return new IndexedCollection<K, C>(coll, keyTransformer, new HashMap<K, C>());
+ return new IndexedCollection<K, C>(coll, keyTransformer,
+ MultiValueMap.<K, C>multiValueMap(new HashMap<K, Collection<C>>()),
+ true);
+ }
+
+ /**
+ * Create an {@link IndexedCollection} for a non-unique index.
+ *
+ * @param <K> the index object type.
+ * @param <C> the collection type.
+ * @param coll the decorated {@link Collection}.
+ * @param keyTransformer the {@link Transformer} for generating index keys.
+ * @return the created {@link IndexedCollection}.
+ */
+ public static <K, C> IndexedCollection<K, C> nonUniqueIndexedCollection(final Collection<C> coll,
+ final Transformer<C, K> keyTransformer) {
+ return new IndexedCollection<K, C>(coll, keyTransformer,
+ MultiValueMap.<K, C>multiValueMap(new HashMap<K, Collection<C>>()),
+ false);
}
/**
- * Create a {@link IndexedCollection} for a unique index.
+ * Create a {@link IndexedCollection}.
*
* @param coll decorated {@link Collection}
* @param keyTransformer {@link Transformer} for generating index keys
* @param map map to use as index
+ * @param uniqueIndex if the index shall enforce uniqueness of index keys
*/
public IndexedCollection(final Collection<C> coll, final Transformer<C, K> keyTransformer,
- final HashMap<K, C> map) {
+ final MultiMap<K, C> map, final boolean uniqueIndex) {
super(coll);
this.keyTransformer = keyTransformer;
- this.index = new HashMap<K, C>();
+ this.index = map;
+ this.uniqueIndex = uniqueIndex;
reindex();
}
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IllegalArgumentException if the object maps to an existing key and the index
+ * enforces a uniqueness constraint
+ */
@Override
public boolean add(final C object) {
final boolean added = super.add(object);
@@ -133,12 +165,30 @@ public class IndexedCollection<K, C> ext
/**
* Get the element associated with the given key.
+ * <p>
+ * In case of a non-unique index, this method will return the first
+ * value associated with the given key. To retrieve all elements associated
+ * with a key, use {@link #values(Object)}.
*
* @param key key to look up
* @return element found
+ * @see #values(Object)
*/
public C get(final K key) {
- return index.get(key);
+ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection
+ Collection<C> coll = (Collection<C>) index.get(key);
+ return coll == null ? null : coll.iterator().next();
+ }
+
+ /**
+ * Get all elements associated with the given key.
+ *
+ * @param key key to look up
+ * @return a collection of elements found, or null if {@code contains(key) == false}
+ */
+ @SuppressWarnings("unchecked") // index is a MultiMap which returns a Collection
+ public Collection<C> values(final K key) {
+ return (Collection<C>) index.get(key);
}
/**
@@ -185,12 +235,15 @@ public class IndexedCollection<K, C> ext
* Provides checking for adding the index.
*
* @param object the object to index
+ * @throws IllegalArgumentException if the object maps to an existing key and the index
+ * enforces a uniqueness constraint
*/
private void addToIndex(final C object) {
- final C existingObject = index.put(keyTransformer.transform(object), object);
- if (existingObject != null) {
+ final K key = keyTransformer.transform(object);
+ if (uniqueIndex && index.containsKey(key)) {
throw new IllegalArgumentException("Duplicate key in uniquely indexed collection.");
}
+ index.put(key, object);
}
/**
Modified: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java?rev=1451883&r1=1451882&r2=1451883&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java (original)
+++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections/collection/IndexedCollectionTest.java Sat Mar 2 12:54:47 2013
@@ -26,7 +26,6 @@ import java.util.List;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.collection.IndexedCollection;
-import org.junit.Test;
/**
* Extension of {@link AbstractCollectionTest} for exercising the
@@ -45,6 +44,10 @@ public class IndexedCollectionTest exten
//------------------------------------------------------------------------
protected Collection<String> decorateCollection(final Collection<String> collection) {
+ return IndexedCollection.nonUniqueIndexedCollection(collection, new IntegerTransformer());
+ }
+
+ protected IndexedCollection<Integer, String> decorateUniqueCollection(final Collection<String> collection) {
return IndexedCollection.uniqueIndexedCollection(collection, new IntegerTransformer());
}
@@ -90,6 +93,14 @@ public class IndexedCollectionTest exten
return list;
}
+ public Collection<String> makeTestCollection() {
+ return decorateCollection(new ArrayList<String>());
+ }
+
+ public Collection<String> makeUniqueTestCollection() {
+ return decorateUniqueCollection(new ArrayList<String>());
+ }
+
@Override
protected boolean skipSerializedCanonicalTests() {
// FIXME: support canonical tests
@@ -98,22 +109,15 @@ public class IndexedCollectionTest exten
//------------------------------------------------------------------------
- @Override
- public void testCollectionAddAll() {
- // FIXME: does not work as we do not support multi-keys yet
- }
-
- @Test
- public void addedObjectsCanBeRetrievedByKey() throws Exception {
- final Collection<String> coll = getCollection();
+ public void testAddedObjectsCanBeRetrievedByKey() throws Exception {
+ final Collection<String> coll = makeTestCollection();
coll.add("12");
coll.add("16");
coll.add("1");
coll.addAll(asList("2","3","4"));
@SuppressWarnings("unchecked")
- final
- IndexedCollection<Integer, String> indexed = (IndexedCollection<Integer, String>) coll;
+ final IndexedCollection<Integer, String> indexed = (IndexedCollection<Integer, String>) coll;
assertEquals("12", indexed.get(12));
assertEquals("16", indexed.get(16));
assertEquals("1", indexed.get(1));
@@ -122,40 +126,43 @@ public class IndexedCollectionTest exten
assertEquals("4", indexed.get(4));
}
- @Test(expected=IllegalArgumentException.class)
- public void ensureDuplicateObjectsCauseException() throws Exception {
- getCollection().add("1");
- getCollection().add("1");
+ public void testEnsureDuplicateObjectsCauseException() throws Exception {
+ final Collection<String> coll = makeUniqueTestCollection();
+
+ coll.add("1");
+ try {
+ coll.add("1");
+ fail();
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testDecoratedCollectionIsIndexedOnCreation() throws Exception {
+ Collection<String> original = makeFullCollection();
+ IndexedCollection<Integer, String> indexed = decorateUniqueCollection(original);
+
+ assertEquals("1", indexed.get(1));
+ assertEquals("2", indexed.get(2));
+ assertEquals("3", indexed.get(3));
}
-// @Test
-// public void decoratedCollectionIsIndexedOnCreation() throws Exception {
-// original.add("1");
-// original.add("2");
-// original.add("3");
-//
-// indexed = IndexedCollection.uniqueIndexedCollection(original, new Transformer<String, Integer>() {
-// public Integer transform(String input) {
-// return Integer.parseInt(input);
-// }
-// });
-// assertEquals("1", indexed.get(1));
-// assertEquals("2", indexed.get(2));
-// assertEquals("3", indexed.get(3));
-// }
-//
-// @Test
-// public void reindexUpdatesIndexWhenTheDecoratedCollectionIsModifiedSeparately() throws Exception {
-// original.add("1");
-// original.add("2");
-// original.add("3");
-//
-// assertNull(indexed.get(1));
-// assertNull(indexed.get(2));
-// assertNull(indexed.get(3));
-// indexed.reindex();
-// assertEquals("1", indexed.get(1));
-// assertEquals("2", indexed.get(2));
-// assertEquals("3", indexed.get(3));
-// }
+ public void testReindexUpdatesIndexWhenDecoratedCollectionIsModifiedSeparately() throws Exception {
+ Collection<String> original = new ArrayList<String>();
+ IndexedCollection<Integer, String> indexed = decorateUniqueCollection(original);
+
+ original.add("1");
+ original.add("2");
+ original.add("3");
+
+ assertNull(indexed.get(1));
+ assertNull(indexed.get(2));
+ assertNull(indexed.get(3));
+
+ indexed.reindex();
+
+ assertEquals("1", indexed.get(1));
+ assertEquals("2", indexed.get(2));
+ assertEquals("3", indexed.get(3));
+ }
}