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 2014/05/30 17:44:09 UTC
svn commit: r1598646 - in /commons/proper/collections/trunk/src:
changes/changes.xml
main/java/org/apache/commons/collections4/CollectionUtils.java
test/java/org/apache/commons/collections4/CollectionUtilsTest.java
Author: tn
Date: Fri May 30 15:44:08 2014
New Revision: 1598646
URL: http://svn.apache.org/r1598646
Log:
[COLLECTIONS-529] Added methods removeAll and retainAll to CollectionUtils that use a custom Equator for equality checks. Thanks to Alexander Muthmann and Dipanjan Laha.
Modified:
commons/proper/collections/trunk/src/changes/changes.xml
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.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=1598646&r1=1598645&r2=1598646&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/changes/changes.xml (original)
+++ commons/proper/collections/trunk/src/changes/changes.xml Fri May 30 15:44:08 2014
@@ -22,6 +22,10 @@
<body>
<release version="4.1" date="TBD" description="">
+ <action issue="COLLECTIONS-529" dev="tn" type="add" due-to="Alexander Muthmann, Dipanjan Laha">
+ Added methods "removeAll(...)" and "retainAll(...)" to "CollectionUtils" that perform
+ equality checks using the provided "Equator" object instead of "Object#equals()".
+ </action>
<action issue="COLLECTIONS-531" dev="tn" type="fix" due-to="Dipanjan Laha">
Use correct type bounds in
"CollectionUtils#isEqualCollection(Collection, Collection, Equator)" to
Modified: commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java?rev=1598646&r1=1598645&r2=1598646&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java (original)
+++ commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/CollectionUtils.java Fri May 30 15:44:08 2014
@@ -579,10 +579,10 @@ public class CollectionUtils {
* @since 4.0
*/
private static class EquatorWrapper<O> {
- private final Equator<O> equator;
+ private final Equator<? super O> equator;
private final O object;
- public EquatorWrapper(final Equator<O> equator, final O object) {
+ public EquatorWrapper(final Equator<? super O> equator, final O object) {
this.equator = equator;
this.object = object;
}
@@ -1682,6 +1682,53 @@ public class CollectionUtils {
}
/**
+ * Returns a collection containing all the elements in
+ * <code>collection</code> that are also in <code>retain</code>. The
+ * cardinality of an element <code>e</code> in the returned collection is
+ * the same as the cardinality of <code>e</code> in <code>collection</code>
+ * unless <code>retain</code> does not contain <code>e</code>, in which case
+ * the cardinality is zero. This method is useful if you do not wish to
+ * modify the collection <code>c</code> and thus cannot call
+ * <code>c.retainAll(retain);</code>.
+ * <p>
+ * Moreover this method uses an {@link Equator} instead of
+ * {@link Object#equals(Object)} to determine the equality of the elements
+ * in <code>collection</code> and <code>retain</code>. Hence this method is
+ * useful in cases where the equals behavior of an object needs to be
+ * modified without changing the object itself.
+ *
+ * @param <E> the type of object the {@link Collection} contains
+ * @param collection the collection whose contents are the target of the {@code retainAll} operation
+ * @param retain the collection containing the elements to be retained in the returned collection
+ * @param equator the Equator used for testing equality
+ * @return a <code>Collection</code> containing all the elements of <code>collection</code>
+ * that occur at least once in <code>retain</code> according to the <code>equator</code>
+ * @throws NullPointerException if any of the parameters is null
+ * @since 4.1
+ */
+ public static <E> Collection<E> retainAll(final Iterable<E> collection,
+ final Iterable<? extends E> retain,
+ final Equator<? super E> equator) {
+
+ final Transformer<E, EquatorWrapper<E>> transformer = new Transformer<E, EquatorWrapper<E>>() {
+ public EquatorWrapper<E> transform(E input) {
+ return new EquatorWrapper<E>(equator, input);
+ }
+ };
+
+ final Set<EquatorWrapper<E>> retainSet =
+ collect(retain, transformer, new HashSet<EquatorWrapper<E>>());
+
+ final List<E> list = new ArrayList<E>();
+ for (final E element : collection) {
+ if (retainSet.contains(new EquatorWrapper<E>(equator, element))) {
+ list.add(element);
+ }
+ }
+ return list;
+ }
+
+ /**
* Removes the elements in <code>remove</code> from <code>collection</code>. That is, this
* method returns a collection containing all the elements in <code>c</code>
* that are not in <code>remove</code>. The cardinality of an element <code>e</code>
@@ -1700,6 +1747,80 @@ public class CollectionUtils {
*/
public static <E> Collection<E> removeAll(final Collection<E> collection, final Collection<?> remove) {
return ListUtils.removeAll(collection, remove);
+ }
+
+ /**
+ * Removes all elements in <code>remove</code> from <code>collection</code>.
+ * That is, this method returns a collection containing all the elements in
+ * <code>collection</code> that are not in <code>remove</code>. The
+ * cardinality of an element <code>e</code> in the returned collection is
+ * the same as the cardinality of <code>e</code> in <code>collection</code>
+ * unless <code>remove</code> contains <code>e</code>, in which case the
+ * cardinality is zero. This method is useful if you do not wish to modify
+ * the collection <code>c</code> and thus cannot call
+ * <code>collection.removeAll(remove)</code>.
+ * <p>
+ * Moreover this method uses an {@link Equator} instead of
+ * {@link Object#equals(Object)} to determine the equality of the elements
+ * in <code>collection</code> and <code>remove</code>. Hence this method is
+ * useful in cases where the equals behavior of an object needs to be
+ * modified without changing the object itself.
+ *
+ * @param <E> the type of object the {@link Collection} contains
+ * @param collection the collection from which items are removed (in the returned collection)
+ * @param remove the items to be removed from the returned collection
+ * @param equator the Equator used for testing equality
+ * @return a <code>Collection</code> containing all the elements of <code>collection</code>
+ * except any element that if equal according to the <code>equator</code>
+ * @throws NullPointerException if any of the parameters is null
+ * @since 4.1
+ */
+ public static <E> Collection<E> removeAll(final Iterable<E> collection,
+ final Iterable<? extends E> remove,
+ final Equator<? super E> equator) {
+
+ final Transformer<E, EquatorWrapper<E>> transformer = new Transformer<E, EquatorWrapper<E>>() {
+ public EquatorWrapper<E> transform(E input) {
+ return new EquatorWrapper<E>(equator, input);
+ }
+ };
+
+ final Set<EquatorWrapper<E>> removeSet =
+ collect(remove, transformer, new HashSet<EquatorWrapper<E>>());
+
+ final List<E> list = new ArrayList<E>();
+ for (final E element : collection) {
+ if (!removeSet.contains(new EquatorWrapper<E>(equator, element))) {
+ list.add(element);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * This method checks, if any of the elements in <code>collection</code> is
+ * equal to <code>object</code>. Object equality is tested with an
+ * <code>equator</code> unlike <code>collection.contains(object)</code>
+ * which uses {@link Object#equals(Object)}.
+ *
+ * @param <E> the type of object the {@link Collection} contains
+ * @param collection the collection from which items are compared
+ * @param object the object to compare with the collection's entries
+ * @param equator the equator to use to check, if the item if equal to any
+ * of the collection's entries.
+ * @return true if <code>object</code> is in <code>collection</code>
+ * according to <code>equator</code>
+ * @throws NullPointerException if any parameter is null
+ * @since 4.1
+ */
+ public static <E> boolean contains(final Collection<? extends E> collection, final E object,
+ final Equator<? super E> equator) {
+ for (final E obj : collection) {
+ if (equator.equate(obj, object)) {
+ return true;
+ }
+ }
+ return false;
}
//-----------------------------------------------------------------------
Modified: commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
URL: http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java?rev=1598646&r1=1598645&r2=1598646&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java (original)
+++ commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java Fri May 30 15:44:08 2014
@@ -18,21 +18,38 @@ package org.apache.commons.collections4;
import static org.apache.commons.collections4.functors.EqualPredicate.equalPredicate;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Vector;
-import org.apache.commons.collections4.queue.CircularFifoQueue;
import org.apache.commons.collections4.bag.HashBag;
import org.apache.commons.collections4.collection.PredicatedCollection;
import org.apache.commons.collections4.collection.SynchronizedCollection;
import org.apache.commons.collections4.collection.TransformedCollection;
import org.apache.commons.collections4.collection.UnmodifiableCollection;
import org.apache.commons.collections4.functors.DefaultEquator;
+import org.apache.commons.collections4.queue.CircularFifoQueue;
import org.junit.Before;
import org.junit.Test;
@@ -1719,16 +1736,153 @@ public class CollectionUtilsTest extends
}
};
assertTrue(CollectionUtils.matchesAll(collectionA, lessThanFive));
-
+
Predicate<Integer> lessThanFour = new Predicate<Integer>() {
public boolean evaluate(Integer object) {
return object < 4;
}
};
assertFalse(CollectionUtils.matchesAll(collectionA, lessThanFour));
-
+
assertTrue(CollectionUtils.matchesAll(null, lessThanFour));
assertTrue(CollectionUtils.matchesAll(emptyCollection, lessThanFour));
}
+ @Test
+ public void testRemoveAllWithEquator() {
+ final List<String> base = new ArrayList<String>();
+ base.add("AC");
+ base.add("BB");
+ base.add("CA");
+
+ final List<String> remove = new ArrayList<String>();
+ remove.add("AA");
+ remove.add("CX");
+ remove.add("XZ");
+
+ // use an equator which compares the second letter only
+ final Collection<String> result = CollectionUtils.removeAll(base, remove, new Equator<String>() {
+
+ public boolean equate(String o1, String o2) {
+ return o1.charAt(1) == o2.charAt(1);
+ }
+
+ public int hash(String o) {
+ return o.charAt(1);
+ }
+ });
+
+ assertEquals(2, result.size());
+ assertTrue(result.contains("AC"));
+ assertTrue(result.contains("BB"));
+ assertFalse(result.contains("CA"));
+ assertEquals(3, base.size());
+ assertEquals(true, base.contains("AC"));
+ assertEquals(true, base.contains("BB"));
+ assertEquals(true, base.contains("CA"));
+ assertEquals(3, remove.size());
+ assertEquals(true, remove.contains("AA"));
+ assertEquals(true, remove.contains("CX"));
+ assertEquals(true, remove.contains("XZ"));
+
+ try {
+ CollectionUtils.removeAll(null, null, DefaultEquator.defaultEquator());
+ fail("expecting NullPointerException");
+ } catch (final NullPointerException npe) {
+ } // this is what we want
+
+ try {
+ CollectionUtils.removeAll(base, remove, null);
+ fail("expecting NullPointerException");
+ } catch (final NullPointerException npe) {
+ } // this is what we want
+ }
+
+ @Test
+ public void testRetainAllWithEquator() {
+ final List<String> base = new ArrayList<String>();
+ base.add("AC");
+ base.add("BB");
+ base.add("CA");
+
+ final List<String> retain = new ArrayList<String>();
+ retain.add("AA");
+ retain.add("CX");
+ retain.add("XZ");
+
+ // use an equator which compares the second letter only
+ final Collection<String> result = CollectionUtils.retainAll(base, retain, new Equator<String>() {
+
+ public boolean equate(String o1, String o2) {
+ return o1.charAt(1) == o2.charAt(1);
+ }
+
+ public int hash(String o) {
+ return o.charAt(1);
+ }
+ });
+ assertEquals(1, result.size());
+ assertTrue(result.contains("CA"));
+ assertFalse(result.contains("BB"));
+ assertFalse(result.contains("AC"));
+
+ assertEquals(3, base.size());
+ assertTrue(base.contains("AC"));
+ assertTrue(base.contains("BB"));
+ assertTrue(base.contains("CA"));
+
+ assertEquals(3, retain.size());
+ assertTrue(retain.contains("AA"));
+ assertTrue(retain.contains("CX"));
+ assertTrue(retain.contains("XZ"));
+
+ try {
+ CollectionUtils.retainAll(null, null, null);
+ fail("expecting NullPointerException");
+ } catch (final NullPointerException npe) {
+ } // this is what we want
+
+ try {
+ CollectionUtils.retainAll(base, retain, null);
+ fail("expecting NullPointerException");
+ } catch (final NullPointerException npe) {
+ } // this is what we want
+ }
+
+ @Test
+ public void testContainsWithEquator() {
+ final List<String> base = new ArrayList<String>();
+ base.add("AC");
+ base.add("BB");
+ base.add("CA");
+
+ final Equator<String> secondLetterEquator = new Equator<String>() {
+
+ public boolean equate(String o1, String o2) {
+ return o1.charAt(1) == o2.charAt(1);
+ }
+
+ public int hash(String o) {
+ return o.charAt(1);
+ }
+
+ };
+
+ assertFalse(base.contains("CC"));
+ assertTrue(CollectionUtils.contains(base, "AC", secondLetterEquator));
+ assertTrue(CollectionUtils.contains(base, "CC", secondLetterEquator));
+ assertFalse(CollectionUtils.contains(base, "CX", secondLetterEquator));
+
+ try {
+ CollectionUtils.contains(null, null, secondLetterEquator);
+ fail("expecting NullPointerException");
+ } catch (final NullPointerException npe) {
+ } // this is what we want
+
+ try {
+ CollectionUtils.contains(base, "AC", null);
+ fail("expecting NullPointerException");
+ } catch (final NullPointerException npe) {
+ } // this is what we want
+ }
}