Author: tn
Date: Mon Jun 22 21:49:27 2015
New Revision: 1686948

URL: http://svn.apache.org/r1686948
Log:
[COLLECTIONS-572] Add set operations to SetUtils.

Modified:
    commons/proper/collections/trunk/src/changes/changes.xml
    
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetUtils.java
    
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/SetUtilsTest.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=1686948&r1=1686947&r2=1686948&view=diff
==============================================================================
--- commons/proper/collections/trunk/src/changes/changes.xml (original)
+++ commons/proper/collections/trunk/src/changes/changes.xml Mon Jun 22 
21:49:27 2015
@@ -22,6 +22,10 @@
   <body>
 
   <release version="4.1" date="TBD" description="">
+    <action issue="COLLECTIONS-572" dev="tn" type="add">
+      Added set operations to "SetUtils": union, difference, intersection and 
disjunction.
+      The operations return a view of the result that is backed by the input 
sets.
+    </action>
     <action issue="COLLECTIONS-570" dev="tn" type="update">
       All constructors and static factory methods will now throw a 
"NullPointerException" if
       a required input argument is null. Previously sometimes a 
"IllegalArgumentException" was used.

Modified: 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetUtils.java
URL: 
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetUtils.java?rev=1686948&r1=1686947&r2=1686948&view=diff
==============================================================================
--- 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetUtils.java
 (original)
+++ 
commons/proper/collections/trunk/src/main/java/org/apache/commons/collections4/SetUtils.java
 Mon Jun 22 21:49:27 2015
@@ -16,9 +16,12 @@
  */
 package org.apache.commons.collections4;
 
+import java.util.AbstractSet;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.IdentityHashMap;
+import java.util.Iterator;
 import java.util.NavigableSet;
 import java.util.Set;
 import java.util.SortedSet;
@@ -68,7 +71,7 @@ public class SetUtils {
      */
     @SuppressWarnings("unchecked") // empty set is OK for any type
     public static <E> SortedSet<E> emptySortedSet() {
-        return (SortedSet<E>) EMPTY_SORTED_SET;
+        return EMPTY_SORTED_SET;
     }
 
     /**
@@ -419,4 +422,223 @@ public class SetUtils {
         return TransformedNavigableSet.transformingNavigableSet(set, 
transformer);
     }
 
+    // Set operations
+    //-----------------------------------------------------------------------
+
+    /**
+     * Returns a unmodifiable <b>view</b> of the union of the given {@link 
Set}s.
+     * <p>
+     * The returned view contains all elements of {@code a} and {@code b}.
+     *
+     * @param <E> the generic type that is able to represent the types 
contained
+     *   in both input sets.
+     * @param a  the first set, must not be null
+     * @param b  the second set, must not be null
+     * @return a view of the union of the two set
+     * @throws NullPointerException if either input set is null
+     * @since 4.1
+     */
+    public static <E> SetView<E> union(final Set<? extends E> a, final Set<? 
extends E> b) {
+        if (a == null || b == null) {
+            throw new NullPointerException("Sets must not be null.");
+        }
+
+        final SetView<E> bMinusA = difference(b, a);
+
+        return new SetView<E>() {
+            @Override
+            public boolean contains(Object o) {
+                return a.contains(o) || b.contains(o);
+            }
+
+            @Override
+            public Iterator<E> createIterator() {
+                return IteratorUtils.chainedIterator(a.iterator(), 
bMinusA.iterator());
+            }
+
+            @Override
+            public boolean isEmpty() {
+                return a.isEmpty() && b.isEmpty();
+            }
+
+            @Override
+            public int size() {
+                return a.size() + bMinusA.size();
+            }
+        };
+    }
+
+    /**
+     * Returns a unmodifiable <b>view</b> containing the difference of the 
given
+     * {@link Set}s, denoted by {@code a \ b} (or {@code a - b}).
+     * <p>
+     * The returned view contains all elements of {@code a} that are not a 
member
+     * of {@code b}.
+     *
+     * @param <E> the generic type that is able to represent the types 
contained
+     *   in both input sets.
+     * @param a  the set to subtract from, must not be null
+     * @param b  the set to subtract, must not be null
+     * @return a view of the relative complement of  of the two sets
+     * @since 4.1
+     */
+    public static <E> SetView<E> difference(final Set<? extends E> a, final 
Set<? extends E> b) {
+        if (a == null || b == null) {
+            throw new NullPointerException("Sets must not be null.");
+        }
+
+        final Predicate<E> notContainedInB = new Predicate<E>() {
+            @Override
+            public boolean evaluate(E object) {
+                return !b.contains(object);
+            }
+        };
+
+        return new SetView<E>() {
+            @Override
+            public boolean contains(Object o) {
+                return a.contains(o) && !b.contains(o);
+            }
+
+            @Override
+            public Iterator<E> createIterator() {
+                return IteratorUtils.filteredIterator(a.iterator(), 
notContainedInB);
+            }
+        };
+    }
+
+    /**
+     * Returns a unmodifiable <b>view</b> of the intersection of the given 
{@link Set}s.
+     * <p>
+     * The returned view contains all elements that are members of both input 
sets
+     * ({@code a} and {@code b}).
+     *
+     * @param <E> the generic type that is able to represent the types 
contained
+     *   in both input sets.
+     * @param a  the first set, must not be null
+     * @param b  the second set, must not be null
+     * @return a view of the intersection of the two sets
+     * @since 4.1
+     */
+    public static <E> SetView<E> intersection(final Set<? extends E> a, final 
Set<? extends E> b) {
+        if (a == null || b == null) {
+            throw new NullPointerException("Sets must not be null.");
+        }
+
+        final Predicate<E> containedInB = new Predicate<E>() {
+            @Override
+            public boolean evaluate(E object) {
+                return b.contains(object);
+            }
+        };
+
+        return new SetView<E>() {
+            @Override
+            public boolean contains(Object o) {
+                return a.contains(o) && b.contains(o);
+            }
+
+            @Override
+            public Iterator<E> createIterator() {
+                return IteratorUtils.filteredIterator(a.iterator(), 
containedInB);
+            }
+        };
+    }
+
+    /**
+     * Returns a unmodifiable <b>view</b> of the symmetric difference of the 
given
+     * {@link Set}s.
+     * <p>
+     * The returned view contains all elements of {@code a} and {@code b} that 
are
+     * not a member of the other set.
+     * <p>
+     * This is equivalent to {@code union(difference(a, b), difference(b, a))}.
+     *
+     * @param <E> the generic type that is able to represent the types 
contained
+     *   in both input sets.
+     * @param a  the first set, must not be null
+     * @param b  the second set, must not be null
+     * @return a view of the symmetric difference of the two sets
+     * @since 4.1
+     */
+    public static <E> SetView<E> disjunction(final Set<? extends E> a, final 
Set<? extends E> b) {
+        if (a == null || b == null) {
+            throw new NullPointerException("Sets must not be null.");
+        }
+
+        final SetView<E> aMinusB = difference(a, b);
+        final SetView<E> bMinusA = difference(b, a);
+
+        return new SetView<E>() {
+            @Override
+            public boolean contains(Object o) {
+                return a.contains(o) ^ b.contains(o);
+            }
+
+            @Override
+            public Iterator<E> createIterator() {
+                return IteratorUtils.chainedIterator(aMinusB.iterator(), 
bMinusA.iterator());
+            }
+
+            @Override
+            public boolean isEmpty() {
+                return aMinusB.isEmpty() && bMinusA.isEmpty();
+            }
+
+            @Override
+            public int size() {
+                return aMinusB.size() + bMinusA.size();
+            }
+        };
+    }
+
+    /**
+     * An unmodifiable <b>view</b> of a set that may be backed by other sets.
+     * <p>
+     * If the decorated sets change, this view will change as well. The 
contents
+     * of this view can be transferred to another instance via the {@link 
#copyInto(Set)}
+     * and {@link #toSet()} methods.
+     * 
+     * @param <E> the element type
+     * @since 4.1
+     */
+    public static abstract class SetView<E> extends AbstractSet<E> {
+
+        @Override
+        public Iterator<E> iterator() {
+            return IteratorUtils.unmodifiableIterator(createIterator());
+        }
+
+        /**
+         * Return an iterator for this view; the returned iterator is
+         * not required to be unmodifiable.
+         * @return a new iterator for this view
+         */
+        protected abstract Iterator<E> createIterator();
+
+        @Override
+        public int size() {
+            return IteratorUtils.size(iterator());
+        }
+
+        /**
+         * Copies the contents of this view into the provided set.
+         *
+         * @param set  the set for copying the contents
+         */
+        public <S extends Set<E>> void copyInto(final S set) {
+            CollectionUtils.addAll(set, this);
+        }
+
+        /**
+         * Returns a new set containing the contents of this view.
+         *
+         * @return a new set containing all elements of this view
+         */
+        public Set<E> toSet() {
+            final Set<E> set = new HashSet<E>(size());
+            copyInto(set);
+            return set;
+        }
+    }
 }

Modified: 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/SetUtilsTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/SetUtilsTest.java?rev=1686948&r1=1686947&r2=1686948&view=diff
==============================================================================
--- 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/SetUtilsTest.java
 (original)
+++ 
commons/proper/collections/trunk/src/test/java/org/apache/commons/collections4/SetUtilsTest.java
 Mon Jun 22 21:49:27 2015
@@ -16,14 +16,20 @@
  */
 package org.apache.commons.collections4;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
 
+import org.apache.commons.collections4.SetUtils.SetView;
 import org.apache.commons.collections4.set.PredicatedSet;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -33,9 +39,32 @@ import org.junit.Test;
  */
 public class SetUtilsTest {
 
+    private Set<Integer> setA;
+    private Set<Integer> setB;
+
+    @Before
+    public void setUp() {
+        setA = new HashSet<Integer>();
+        setA.add(1);
+        setA.add(2);
+        setA.add(3);
+        setA.add(4);
+        setA.add(5);
+
+        setB = new HashSet<Integer>();
+        setB.add(3);
+        setB.add(4);
+        setB.add(5);
+        setB.add(6);
+        setB.add(7);
+    }
+
+    //-----------------------------------------------------------------------
+
     @Test
     public void testpredicatedSet() {
         final Predicate<Object> predicate = new Predicate<Object>() {
+            @Override
             public boolean evaluate(final Object o) {
                 return o instanceof String;
             }
@@ -112,4 +141,118 @@ public class SetUtilsTest {
         set.remove(a);
         assertEquals(2, set.size());
     }
+
+    @Test
+    public void union() {
+        final SetView<Integer> set = SetUtils.union(setA, setB);
+        assertEquals(7, set.size());
+        assertTrue(set.containsAll(setA));
+        assertTrue(set.containsAll(setB));
+
+        final Set<Integer> set2 = SetUtils.union(setA, 
SetUtils.<Integer>emptySet());
+        assertEquals(setA, set2);
+
+        try {
+            SetUtils.union(setA, null);
+            fail("Expecting NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+
+        try {
+            SetUtils.union(null, setA);
+            fail("Expecting NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+    }
+
+    @Test
+    public void difference() {
+        final SetView<Integer> set = SetUtils.difference(setA, setB);
+        assertEquals(2, set.size());
+        assertTrue(set.contains(1));
+        assertTrue(set.contains(2));
+        for (Integer i : setB) {
+            assertFalse(set.contains(i));
+        }
+
+        final Set<Integer> set2 = SetUtils.difference(setA, 
SetUtils.<Integer>emptySet());
+        assertEquals(setA, set2);
+
+        try {
+            SetUtils.difference(setA, null);
+            fail("Expecting NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+
+        try {
+            SetUtils.difference(null, setA);
+            fail("Expecting NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+    }
+
+    @Test
+    public void intersection() {
+        final SetView<Integer> set = SetUtils.intersection(setA, setB);
+        assertEquals(3, set.size());
+        assertTrue(set.contains(3));
+        assertTrue(set.contains(4));
+        assertTrue(set.contains(5));
+        assertFalse(set.contains(1));
+        assertFalse(set.contains(2));
+        assertFalse(set.contains(6));
+        assertFalse(set.contains(7));
+
+        final Set<Integer> set2 = SetUtils.intersection(setA, 
SetUtils.<Integer>emptySet());
+        assertEquals(SetUtils.<Integer>emptySet(), set2);
+
+        try {
+            SetUtils.intersection(setA, null);
+            fail("Expecting NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+
+        try {
+            SetUtils.intersection(null, setA);
+            fail("Expecting NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+    }
+
+    @Test
+    public void disjunction() {
+        final SetView<Integer> set = SetUtils.disjunction(setA, setB);
+        assertEquals(4, set.size());
+        assertTrue(set.contains(1));
+        assertTrue(set.contains(2));
+        assertTrue(set.contains(6));
+        assertTrue(set.contains(7));
+        assertFalse(set.contains(3));
+        assertFalse(set.contains(4));
+        assertFalse(set.contains(5));
+
+        final Set<Integer> set2 = SetUtils.disjunction(setA, 
SetUtils.<Integer>emptySet());
+        assertEquals(setA, set2);
+
+        try {
+            SetUtils.disjunction(setA, null);
+            fail("Expecting NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+
+        try {
+            SetUtils.disjunction(null, setA);
+            fail("Expecting NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+    }
+
 }


Reply via email to