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
+      }
 }