diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 2adc1862b..eaf07949b 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -22,6 +22,9 @@ + + Added method "CollectionUtils#isEqualCollection(Collection, Collection, Equator)". + Tree traversal with a TreeListIterator will not be affected anymore by the removal of an element directly after a call to previous(). diff --git a/src/main/java/org/apache/commons/collections/CollectionUtils.java b/src/main/java/org/apache/commons/collections/CollectionUtils.java index 4657eb927..ac97191e8 100644 --- a/src/main/java/org/apache/commons/collections/CollectionUtils.java +++ b/src/main/java/org/apache/commons/collections/CollectionUtils.java @@ -34,6 +34,7 @@ import org.apache.commons.collections.collection.SynchronizedCollection; import org.apache.commons.collections.collection.TransformedCollection; import org.apache.commons.collections.collection.UnmodifiableBoundedCollection; import org.apache.commons.collections.collection.UnmodifiableCollection; +import org.apache.commons.collections.functors.Equator; import org.apache.commons.collections.functors.TruePredicate; /** @@ -522,6 +523,78 @@ public class CollectionUtils { return true; } + /** + * Returns true iff the given {@link Collection}s contain + * exactly the same elements with exactly the same cardinalities. + *

+ * That is, iff the cardinality of e in a is + * equal to the cardinality of e in b, + * for each element e in a or b. + * + * @param a the first collection, must not be null + * @param b the second collection, must not be null + * @param equator the Equator used for testing equality + * @return true iff the collections contain the same elements with the same cardinalities. + * @throws IllegalArgumentException if the equator is null + * @since 4.0 + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) // we don't know the types due to wildcards in the signature + public static boolean isEqualCollection(final Collection a, final Collection b, final Equator equator) { + if (equator == null) { + throw new IllegalArgumentException("equator may not be null"); + } + + if(a.size() != b.size()) { + return false; + } + + final Transformer transformer = new Transformer() { + public EquatorWrapper transform(Object input) { + return new EquatorWrapper(equator, input); + } + }; + + return isEqualCollection(collect(a, transformer), collect(b, transformer)); + } + + /** + * Wraps another object and uses the provided Equator to implement + * {@link #equals(Object)} and {@link #hashCode()}. + *

+ * This class can be used to store objects into a Map. + * + * @param the element type + * @since 4.0 + */ + private static class EquatorWrapper { + private final Equator equator; + private final O object; + + public EquatorWrapper(final Equator equator, final O object) { + this.equator = equator; + this.object = object; + } + + public O getObject() { + return object; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof EquatorWrapper)) { + return false; + } + @SuppressWarnings("unchecked") + EquatorWrapper otherObj = (EquatorWrapper) obj; + return equator.equate(object, otherObj.getObject()); + } + + @Override + public int hashCode() { + return equator.hash(object); + } + } + /** * Returns the number of occurrences of obj in coll. * diff --git a/src/test/java/org/apache/commons/collections/CollectionUtilsTest.java b/src/test/java/org/apache/commons/collections/CollectionUtilsTest.java index 8dc7433c5..2d649e9ed 100644 --- a/src/test/java/org/apache/commons/collections/CollectionUtilsTest.java +++ b/src/test/java/org/apache/commons/collections/CollectionUtilsTest.java @@ -32,6 +32,8 @@ import org.apache.commons.collections.collection.PredicatedCollection; import org.apache.commons.collections.collection.SynchronizedCollection; import org.apache.commons.collections.collection.TransformedCollection; import org.apache.commons.collections.collection.UnmodifiableCollection; +import org.apache.commons.collections.functors.DefaultEquator; +import org.apache.commons.collections.functors.Equator; import org.junit.Before; import org.junit.Test; @@ -515,6 +517,36 @@ public class CollectionUtilsTest extends MockTestCase { assertTrue(CollectionUtils.isEqualCollection(b, a)); } + @Test + public void testIsEqualCollectionEquator() { + Collection collB = CollectionUtils.collect(collectionB, TRANSFORM_TO_INTEGER); + + // odd / even equator + final Equator e = new Equator() { + public boolean equate(Integer o1, Integer o2) { + if (o1.intValue() % 2 == 0 ^ o2.intValue() % 2 == 0) return false; + else return true; + } + + public int hash(Integer o) { + return o.intValue() % 2 == 0 ? Integer.valueOf(0).hashCode() : Integer.valueOf(1).hashCode(); + } + }; + + assertTrue(CollectionUtils.isEqualCollection(collectionA, collectionA, e)); + assertTrue(CollectionUtils.isEqualCollection(collectionA, collB, e)); + assertTrue(CollectionUtils.isEqualCollection(collB, collectionA, e)); + + final Equator defaultEquator = new DefaultEquator(); + assertFalse(CollectionUtils.isEqualCollection(collectionA, collectionB, defaultEquator)); + assertFalse(CollectionUtils.isEqualCollection(collectionA, collB, defaultEquator)); + } + + @Test(expected=IllegalArgumentException.class) + public void testIsEqualCollectionNullEquator() { + CollectionUtils.isEqualCollection(collectionA, collectionA, null); + } + @Test public void testIsProperSubCollection() { final Collection a = new ArrayList();