Added initial IndexedCollection - still needs quite a bit of work - see TODO comments
JIRA: COLLECTIONS-275 git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/branches/collections_jdk5_branch@593347 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
cf3ddae273
commit
f913f600ad
|
@ -0,0 +1,116 @@
|
|||
package org.apache.commons.collections;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.commons.collections.collection.AbstractCollectionDecorator;
|
||||
|
||||
/**
|
||||
* An IndexedCollection is a Map-like view onto a Collection. It accepts a
|
||||
* keyTransformer to define how the keys are converted from the values.
|
||||
* <p>
|
||||
* Modifications made to this decorator modify the index as well as the
|
||||
* decorated {@link Collection}. However, modifications to the underlying
|
||||
* {@link Collection} will not updated the index and it will get out of sync.
|
||||
* <p>
|
||||
* If modification to the decorated {@link Collection} is unavoidable, then a
|
||||
* call to {@link #reindex()} will update the index to the current contents of
|
||||
* the {@link Collection}.
|
||||
*
|
||||
* @param K the type of object in the index.
|
||||
* @param C the type of object in the collection.
|
||||
* @author Stephen Kestle
|
||||
*/
|
||||
// TODO support MultiMap/non-unique index behavior
|
||||
// TODO add support for remove and clear
|
||||
public class IndexedCollection<K, C> extends AbstractCollectionDecorator<C> {
|
||||
/**
|
||||
* .
|
||||
*/
|
||||
private static final long serialVersionUID = -5512610452568370038L;
|
||||
|
||||
/**
|
||||
* Create an {@link IndexedCollection} for a 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> uniqueIndexedCollection(final Collection<C> coll, final Transformer<C, K> keyTransformer) {
|
||||
return new IndexedCollection<K, C>(coll, keyTransformer, new HashMap<K, C>());
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Transformer} for generating index keys.
|
||||
*/
|
||||
private final Transformer<C, K> keyTransformer;
|
||||
|
||||
/**
|
||||
* The map of indexes to collected objects.
|
||||
*/
|
||||
private final HashMap<K, C> index;
|
||||
|
||||
/**
|
||||
* Create a {@link IndexedCollection} for a unique index.
|
||||
*
|
||||
* @param coll the decorated {@link Collection}.
|
||||
* @param keyTransformer the {@link Transformer} for generating index keys.
|
||||
* @return the created {@link IndexedCollection}.
|
||||
*/
|
||||
public IndexedCollection(Collection<C> coll, Transformer<C, K> keyTransformer, HashMap<K, C> map) {
|
||||
super(coll);
|
||||
this.keyTransformer = keyTransformer;
|
||||
this.index = map;
|
||||
reindex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the index and re-indexes the entire decorated {@link Collection}.
|
||||
*/
|
||||
public void reindex() {
|
||||
index.clear();
|
||||
for (C c : decorated()) {
|
||||
addIndex(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an object to the collection and index.
|
||||
*/
|
||||
@Override
|
||||
// TODO: Add error handling for when super.add() fails
|
||||
public boolean add(C object) {
|
||||
addIndex(object);
|
||||
return super.add(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entire collection to the collection and index.
|
||||
*/
|
||||
@Override
|
||||
// TODO: Add error handling for when super.addAll() fails
|
||||
public boolean addAll(Collection<? extends C> coll) {
|
||||
for (C c : coll) {
|
||||
addIndex(c);
|
||||
}
|
||||
return super.addAll(coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides checking for adding the index.
|
||||
*
|
||||
* @param object the object to index.
|
||||
*/
|
||||
private void addIndex(C object) {
|
||||
final C existingObject = index.put(keyTransformer.transform(object), object);
|
||||
if (existingObject != null) {
|
||||
throw new IllegalArgumentException("Duplicate key in uniquely indexed collection.");
|
||||
}
|
||||
}
|
||||
|
||||
public C get(K key) {
|
||||
return index.get(key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package org.apache.commons.collections;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
public abstract class AbstractDecoratedCollectionTest<C> {
|
||||
/**
|
||||
* The {@link Collection} being decorated.
|
||||
*/
|
||||
protected Collection<C> original;
|
||||
/**
|
||||
* The Collection under test that decorates {@link #original}.
|
||||
*/
|
||||
protected Collection<C> decorated;
|
||||
|
||||
@Before
|
||||
public void setUpDecoratedCollection() throws Exception {
|
||||
original = new ArrayList<C>();
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ import org.junit.runners.Suite.SuiteClasses;
|
|||
TestSetUtils.class,
|
||||
TestTransformerUtils.class,
|
||||
TestArrayStack.class,
|
||||
TestExtendedProperties.class})
|
||||
TestExtendedProperties.class,
|
||||
TestIndexedCollection.class})
|
||||
public class TestAll extends TestCase {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package org.apache.commons.collections;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestIndexedCollection extends AbstractDecoratedCollectionTest<String> {
|
||||
private IndexedCollection<Integer, String> indexed;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
indexed = IndexedCollection.uniqueIndexedCollection(original, new Transformer<String, Integer>() {
|
||||
public Integer transform(String input) {
|
||||
return Integer.parseInt(input);
|
||||
}
|
||||
});
|
||||
decorated = indexed;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addedObjectsCanBeRetrievedByKey() throws Exception {
|
||||
decorated.add("12");
|
||||
decorated.add("16");
|
||||
decorated.add("1");
|
||||
decorated.addAll(asList("2","3","4"));
|
||||
assertEquals("12", indexed.get(12));
|
||||
assertEquals("16", indexed.get(16));
|
||||
assertEquals("1", indexed.get(1));
|
||||
assertEquals("2", indexed.get(2));
|
||||
assertEquals("3", indexed.get(3));
|
||||
assertEquals("4", indexed.get(4));
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void ensureDuplicateObjectsCauseException() throws Exception {
|
||||
decorated.add("1");
|
||||
decorated.add("1");
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue