[COLLECTIONS-529] Added methods removeAll and retainAll to CollectionUtils that use a custom Equator for equality checks. Thanks to Alexander Muthmann and Dipanjan Laha.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1598646 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
bb0a4b6b03
commit
0a7871a141
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -1681,6 +1681,53 @@ public class CollectionUtils {
|
|||
return ListUtils.retainAll(collection, retain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
|
|
@ -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 MockTestCase {
|
|||
}
|
||||
};
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue