You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2023/07/31 03:22:34 UTC

[groovy] branch master updated: GROOVY-11127: Set operator extension methods (#1915)

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/master by this push:
     new 397aedd0e8 GROOVY-11127: Set operator extension methods (#1915)
397aedd0e8 is described below

commit 397aedd0e8a15b18c623ceb28e36a6c66e8e6dec
Author: Spencer Allain <sp...@yahoo.com>
AuthorDate: Sun Jul 30 23:22:28 2023 -0400

    GROOVY-11127: Set operator extension methods (#1915)
    
    * add set-specific operators
    
    * adjust comment in tests
    
    * add Comparator variants
    
    * base union code
    
    * Update DefaultGroovyMethods.java
    
    ---------
    
    Co-authored-by: Paul King <pa...@asert.com.au>
---
 .../groovy/runtime/DefaultGroovyMethods.java       | 489 +++++++++++++++++++++
 src/test/groovy/GroovyMethodsTest.groovy           | 136 ++++++
 2 files changed, 625 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index b532fc6ed3..7817fe14cf 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -10881,6 +10881,258 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
         return true;
     }
 
+    /**
+     * Create a Collection composed of the union of both collections.  Any
+     * elements that exist in either collections are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * For collections of custom objects; the objects should implement java.lang.Comparable
+     * <pre class="groovyTestCase">assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])</pre>
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element exists in the resultant collection.
+     *
+     * @param left  a Collection
+     * @param right a Collection
+     * @return a Collection as a union of both collections
+     * @see #union(Collection, Collection, Comparator)
+     * @since 5.0.0
+     */
+    public static <T> Collection<T> union(Collection<T> left, Collection<T> right) {
+        return union(left, right, new NumberAwareComparator<>());
+    }
+
+    /**
+     * Create a Collection composed of the union of both collections.  Any
+     * elements that exist in either collections are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * For collections of custom objects; the objects should implement java.lang.Comparable
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6], Comparator.naturalOrder())
+     * assert [4,8,12,16,20,1,3] == [4,8,12,16,20].union([1,2,3,4], (x, y) {@code -> x * x <=> y})
+     * </pre>
+     * <pre class="groovyTestCase">
+     * def one = ['a', 'B', 'c', 'd']
+     * def two = ['b', 'C', 'd', 'e']
+     * def compareIgnoreCase = { a, b {@code ->} a.toLowerCase() {@code <=>} b.toLowerCase() }
+     * assert one.union(two) == ['a', 'B', 'c', 'd', 'b', 'C', 'e']
+     * assert two.union(one) == ['b', 'C', 'd', 'e', 'a', 'B', 'c']
+     * assert one.union(two, compareIgnoreCase) == ['a', 'B', 'c', 'd', 'e']
+     * assert two.union(one, compareIgnoreCase) == ['b', 'C', 'd', 'e', 'a']
+     * </pre>
+     *
+     * @param left  a Collection
+     * @param right a Collection
+     * @param comparator a Comparator
+     * @return a Collection as a union of both collections
+     * @since 5.0.0
+     */
+    public static <T> Collection<T> union(Collection<T> left, Collection<T> right, Comparator<? super T> comparator) {
+        if (left.isEmpty() && right.isEmpty()) {
+            return createSimilarCollection(left, 0);
+        }
+
+        Collection<T> result = createSimilarCollection(left, left.size() + right.size());
+        //creates the collection to look for values.
+        Collection<T> compareWith = new TreeSet<>(comparator);
+
+        for (final T leftElement : left) {
+            if (compareWith.add(leftElement)) {
+                result.add(leftElement);
+            }
+        }
+
+        for (final T rightElement : right) {
+            if (compareWith.add(rightElement)) {
+                result.add(rightElement);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Create a Collection composed of the union of both iterables.  Any
+     * elements that exist in either iterables are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * For iterables of custom objects; the objects should implement java.lang.Comparable
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])
+     * </pre>
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element already exists in the resultant collection.
+     *
+     * @param left  an Iterable
+     * @param right an Iterable
+     * @return a Collection as a union of both iterables
+     * @see #union(Iterable, Iterable, Comparator)
+     * @since 5.0.0
+     */
+    public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right) {
+        return union(asCollection(left), asCollection(right));
+    }
+
+    /**
+     * Create a Collection composed of the union of both iterables.  Any
+     * elements that exist in either iterables are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * For iterables of custom objects; the objects should implement java.lang.Comparable
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6], Comparator.naturalOrder())
+     * </pre>
+     *
+     * @param left  an Iterable
+     * @param right an Iterable
+     * @param comparator a Comparator
+     * @return a Collection as a union of both iterables
+     * @since 5.0.0
+     */
+    public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+        return union(asCollection(left), asCollection(right), comparator);
+    }
+
+    /**
+     * Create a Collection composed of the union of both iterables.
+     * Elements from the first iterable and second iterable are added to the result if the elements are not
+     * already in the result (according to the comparator closure).
+     * If the closure takes a single parameter, the argument passed will be each element,
+     * and the closure should return a value used for comparison (either using
+     * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+     * If the closure takes two parameters, two items from the Iterator
+     * will be passed as arguments, and the closure should return an
+     * int value (with 0 indicating the items are deemed equal).
+     * <pre class="groovyTestCase">
+     * def one = ['a', 'B', 'c', 'd']
+     * def two = ['b', 'C', 'd', 'e']
+     * def compareIgnoreCase = { it.toLowerCase() }
+     * assert one.union(two, compareIgnoreCase) == ['a', 'B', 'c', 'd', 'e']
+     * assert two.union(one, compareIgnoreCase) == ['b', 'C', 'd', 'e', 'a']
+     * </pre>
+     *
+     * @param left  an Iterable
+     * @param right an Iterable
+     * @param condition a Closure used to determine unique items
+     * @return a Collection as a union of both iterables
+     * @since 5.0.0
+     */
+    public static <T> Collection<T> union(Iterable<T> left, Iterable<T> right, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
+        Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
+            ? new OrderBy<>(condition, true)
+            : new ClosureComparator<>(condition);
+        return union(left, right, comparator);
+    }
+
+    /**
+     * Create a List composed of the union of a List and an Iterable.  Any
+     * elements that exist in both iterables are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6,7,8] == [1,2,3,4,5].union([4,5,6,7,8])
+     * </pre>
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element already exists in the resultant collection.
+     *
+     * @param left  a List
+     * @param right an Iterable
+     * @return a List as a union of a List and an Iterable
+     * @see #union(List, Iterable, Comparator)
+     * @since 5.0.0
+     */
+    public static <T> List<T> union(List<T> left, Iterable<T> right) {
+        return (List<T>) union((Collection<T>) left, asCollection(right));
+    }
+
+    /**
+     * Create a List composed of the union of a List and an Iterable.  Any
+     * elements that exist in either iterables are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6] == [1,2,3,4].union([3,4,5,6])
+     * </pre>
+     *
+     * @param left  a List
+     * @param right an Iterable
+     * @param comparator a Comparator
+     * @return a List as a union of a List and an Iterable
+     * @since 5.0.0
+     */
+    public static <T> List<T> union(List<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+        return (List<T>) union((Collection<T>) left, asCollection(right), comparator);
+    }
+
+    /**
+     * Create a Set composed of the union of a Set and an Iterable.  Any
+     * elements that exist in either iterables are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6,7,8] as Set == ([1,2,3,4,5] as Set).union([4,5,6,7,8])
+     * </pre>
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element already exists in the resultant collection.
+     *
+     * @param left  a Set
+     * @param right an Iterable
+     * @return a Set as a union of a Set and an Iterable
+     * @see #union(Set, Iterable, Comparator)
+     * @since 5.0.0
+     */
+    public static <T> Set<T> union(Set<T> left, Iterable<T> right) {
+        return (Set<T>) union((Collection<T>) left, asCollection(right));
+    }
+
+    /**
+     * Create a Set composed of the union of a Set and an Iterable.  Any
+     * elements that exist in either iterables are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6] as Set == ([1,2,3,4] as Set).union([3,4,5,6], Comparator.naturalOrder())
+     * </pre>
+     *
+     * @param left  a Set
+     * @param right an Iterable
+     * @param comparator a Comparator
+     * @return a Set as a union of a Set and an Iterable
+     * @since 5.0.0
+     */
+    public static <T> Set<T> union(Set<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+        return (Set<T>) union((Collection<T>) left, asCollection(right), comparator);
+    }
+
+    /**
+     * Create a SortedSet composed of the union of a SortedSet and an Iterable.  Any
+     * elements that exist in either iterables are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6,7,8] as SortedSet == ([1,2,3,4,5] as SortedSet).union([4,5,6,7,8])
+     * </pre>
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element already exists in the resultant collection.
+     *
+     * @param left  a SortedSet
+     * @param right an Iterable
+     * @return a Set as a union of a SortedSet and an Iterable
+     * @see #intersect(SortedSet, Iterable, Comparator)
+     * @since 5.0.0
+     */
+    public static <T> SortedSet<T> union(SortedSet<T> left, Iterable<T> right) {
+        return (SortedSet<T>) union((Collection<T>) left, asCollection(right));
+    }
+
+    /**
+     * Create a SortedSet composed of the intersection of a SortedSet and an Iterable.  Any
+     * elements that exist in either iterables are added to the resultant collection, such
+     * that no elements are duplicated in the resultant collection.
+     * <pre class="groovyTestCase">
+     * assert [1,2,3,4,5,6,7,8] as SortedSet == ([1,2,3,4,5] as SortedSet).union([4,5,6,7,8])
+     * </pre>
+     *
+     * @param left  a SortedSet
+     * @param right an Iterable
+     * @param comparator a Comparator
+     * @return a Set as a union of a SortedSet and an Iterable
+     * @since 2.5.0
+     */
+    public static <T> SortedSet<T> union(SortedSet<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+        return (SortedSet<T>) union((Collection<T>) left, asCollection(right), comparator);
+    }
+
     /**
      * Chops the Iterable into pieces, returning lists with sizes corresponding to the supplied chop sizes.
      * If the Iterable isn't large enough, truncated (possibly empty) pieces are returned.
@@ -14709,6 +14961,243 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
         return sw.toString();
     }
 
+    /**
+     * Create a Set as a union of a Set and an Iterable.  Any
+     * elements that exist in either are added to the resultant Set.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * def a = [1,2,3,4] as Set
+     * def b = [3,4,5,6] as Set
+     * assert (a | b) == [1,2,3,4,5,6] as Set
+     * </pre>
+     *
+     * @param left  the left Set
+     * @param right the right Iterable
+     * @return the merged Set
+     * @since 5.0.0
+     * @see #plus(Set, Iterable)
+     */
+    public static <T> Set<T> or(Set<T> left, Iterable<T> right) {
+        return plus(left, right);
+    }
+
+    /**
+     * Create a SortedSet as a union of a SortedSet and an Iterable.  Any
+     * elements that exist in either are added to the resultant SortedSet.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * def a = [1,2,3,4] as SortedSet
+     * def b = [3,4,5,6] as Set
+     * assert (a | b) == [1,2,3,4,5,6] as SortedSet
+     * </pre>
+     *
+     * @param left  the left SortedSet
+     * @param right the right Iterable
+     * @return the merged SortedSet
+     * @since 5.0.0
+     * @see #plus(SortedSet, Iterable)
+     */
+    public static <T> SortedSet<T> or(SortedSet<T> left, Iterable<T> right) {
+        return plus(left, right);
+    }
+
+    /**
+     * Create a Set composed of the intersection of a Set and an Iterable.  Any
+     * elements that exist in both are added to the resultant Set.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * def a = [1,2,3,4] as Set
+     * def b = [3,4,5,6] as Set
+     * assert (a & b) == [3,4] as Set
+     * </pre>
+     *
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element exists in both sets.
+     *
+     * @param left  a Set
+     * @param right an Iterable
+     * @return a Set as an intersection of a Set and an Iterable
+     * @see #intersect(Set, Iterable)
+     * @since 5.0.0
+     */
+    public static <T> Set<T> and(Set<T> left, Iterable<T> right) {
+        return intersect(left, right);
+    }
+
+    /**
+     * Create a Set composed of the intersection of a Set and an Iterable.  Any
+     * elements that exist in both iterables are added to the resultant collection.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * assert [3,4] as Set == ([1,2,3,4] as Set).and([3,4,5,6], Comparator.naturalOrder())
+     * </pre>
+     *
+     * @param left  a Set
+     * @param right an Iterable
+     * @param comparator a Comparator
+     * @return a Set as an intersection of a Set and an Iterable
+     * @see #intersect(Set, Iterable, Comparator)
+     * @since 5.0.0
+     */
+    public static <T> Set<T> and(Set<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+        return intersect(left, right, comparator);
+    }
+
+    /**
+     * Create a SortedSet composed of the intersection of a SortedSet and an Iterable.  Any
+     * elements that exist in both are added to the resultant SortedSet.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * def a = [1,2,3,4] as SortedSet
+     * def b = [3,4,5,6] as Set
+     * assert (a & b) == [3,4] as SortedSet
+     * </pre>
+     *
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element exists in both sets.
+     *
+     * @param left  a SortedSet
+     * @param right an Iterable
+     * @return a SortedSet as an intersection of a SortedSet and an Iterable
+     * @see #intersect(Set, Iterable)
+     * @since 5.0.0
+     */
+    public static <T> SortedSet<T> and(SortedSet<T> left, Iterable<T> right) {
+        return intersect(left, right);
+    }
+
+    /**
+     * Create a SortedSet composed of the intersection of a SortedSet and an Iterable.  Any
+     * elements that exist in both iterables are added to the resultant collection.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * assert [3,4] as SortedSet == ([1,2,3,4] as SortedSet).and([3,4,5,6], Comparator.naturalOrder())
+     * </pre>
+     *
+     * @param left  a SortedSet
+     * @param right an Iterable
+     * @param comparator a Comparator
+     * @return a SortedSet as an intersection of a SortedSet and an Iterable
+     * @see #intersect(SortedSet, Iterable, Comparator)
+     * @since 5.0.0
+     */
+    public static <T> SortedSet<T> and(SortedSet<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+        return intersect(left, right, comparator);
+    }
+
+    /**
+     * Create a Set composed of the symmetric difference of a Set and an Iterable.  Any
+     * elements that exit in only one are added to the resultant Set.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * def a = [1,2,3,4] as Set
+     * def b = [3,4,5,6] as Set
+     * assert (a ^ b) == [1,2,5,6] as Set
+     * </pre>
+     *
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element exists in both sets.
+     *
+     * @param left  a Set
+     * @param right an Iterable
+     * @return a Set as a symmetric difference of a Set and an Iterable
+     * @since 5.0.0
+     */
+    public static <T> Set<T> xor(Set<T> left, Iterable<T> right) {
+        Collection<T> rightCollection = asCollection(right); // ensure the iterable can be iterated over more than once
+        return minus(or(left, rightCollection), and(left, rightCollection));
+    }
+
+    /**
+     * Create a Set composed of the symmetric difference of a Set and an Iterable.  Any
+     * elements that exit in only one are added to the resultant Set.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * assert [1,2,5,6] as Set == ([1,2,3,4] as Set).xor([3,4,5,6], Comparator.naturalOrder())
+     * </pre>
+     *
+     * @param left  a Set
+     * @param right an Iterable
+     * @param comparator a Comparator
+     * @return a Set as a symmetric difference of a Set and an Iterable
+     * @since 5.0.0
+     */
+    public static <T> Set<T> xor(Set<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+        Collection<T> rightCollection = asCollection(right); // ensure the iterable can be iterated over more than once
+        return (Set<T>) minus(or(left, rightCollection), and(left, rightCollection, comparator), comparator);
+    }
+
+    /**
+     * Create a SortedSet composed of the symmetric difference of a SortedSet and an Iterable.  Any
+     * elements that exist in only one of the sets are added to the resultant SortedSet.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * def a = [1,2,3,4] as SortedSet
+     * def b = [3,4,5,6] as Set
+     * assert (a ^ b) == [1,2,5,6] as SortedSet
+     * </pre>
+     *
+     * By default, Groovy uses a {@link NumberAwareComparator} when determining if an
+     * element exists in both sets.
+     *
+     * @param left  a SortedSet
+     * @param right an Iterable
+     * @return a SortedSet as a symmetric difference of a SortedSet and an Iterable
+     * @since 5.0.0
+     */
+    public static <T> SortedSet<T> xor(SortedSet<T> left, Iterable<T> right) {
+        Collection<T> rightCollection = asCollection(right); // ensure the iterable can be iterated over more than once
+        return minus(or(left, rightCollection), and(left, rightCollection));
+    }
+
+    /**
+     * Create a SortedSet composed of the symmetric difference of a SortedSet and an Iterable.  Any
+     * elements that exit in only one are added to the resultant SortedSet.
+     * <p>
+     * This operation will always create a new object for the result,
+     * while the operands remain unchanged.
+     *
+     * <pre class="groovyTestCase">
+     * assert [1,2,5,6] as SortedSet == ([1,2,3,4] as SortedSet).xor([3,4,5,6], Comparator.naturalOrder())
+     * </pre>
+     *
+     * @param left  a SortedSet
+     * @param right an Iterable
+     * @param comparator a Comparator
+     * @return a SortedSet as a symmetric difference of a SortedSet and an Iterable
+     * @since 5.0.0
+     */
+    public static <T> SortedSet<T> xor(SortedSet<T> left, Iterable<T> right, Comparator<? super T> comparator) {
+        Collection<T> rightCollection = asCollection(right); // ensure the iterable can be iterated over more than once
+        return (SortedSet<T>) minus(or(left, rightCollection), and(left, rightCollection, comparator), comparator);
+    }
     //--------------------------------------------------------------------------
     // attic
 
diff --git a/src/test/groovy/GroovyMethodsTest.groovy b/src/test/groovy/GroovyMethodsTest.groovy
index 0592e73e94..15f8ba18dd 100644
--- a/src/test/groovy/GroovyMethodsTest.groovy
+++ b/src/test/groovy/GroovyMethodsTest.groovy
@@ -1837,6 +1837,142 @@ class GroovyMethodsTest extends GroovyTestCase {
         assert [1, 2, 1, 2, 1, 2] == iterable * 3
     }
 
+    void testUnionForSets() {
+        Set empty = [] as Set
+        Set a = [1,2,3,4] as Set
+        Set b = [3,4,5,6] as Set
+        Set c = [5,6,7,8] as SortedSet
+
+        // NOTE: == has higher precedence than |, so need to use parens to test correct logic
+        assert (a | b) == [1,2,3,4,5,6] as Set
+        assert (b | a) == [1,2,3,4,5,6] as Set
+        assert (a | c) == [1,2,3,4,5,6,7,8] as Set
+        assert (b | c) == [3,4,5,6,7,8] as Set
+        assert (empty | a) == a
+        assert (a | empty) == a
+
+        assert (a | b) instanceof Set
+        assert (a | c) !instanceof SortedSet
+    }
+
+    void testUnionForSortedSets() {
+        SortedSet empty = [] as SortedSet
+        SortedSet a = [1,2,3,4] as SortedSet
+        Set b = [3,4,5,6] as Set
+        Set c = [5,6,7,8] as SortedSet
+
+        // NOTE: == has higher precedence than |, so need to use parens to test correct logic
+        assert (a | b) == [1,2,3,4,5,6] as SortedSet
+        assert (a | c) == [1,2,3,4,5,6,7,8] as SortedSet
+        assert (c | a) == [1,2,3,4,5,6,7,8] as SortedSet
+        assert (c | b) == [3,4,5,6,7,8] as SortedSet
+        assert (empty | a) == a
+        assert (a | empty) == a
+
+        assert (a | b) instanceof SortedSet
+        assert (a | c) instanceof SortedSet
+    }
+
+    void testInersectionForSets() {
+        Set empty = [] as Set
+        Set a = [1,2,3,4] as Set
+        Set b = [3,4,5,6] as Set
+        Set c = [5,6,7,8] as SortedSet
+
+        // NOTE: == has higher precedence than &, so need to use parens to test correct logic
+        assert (a & b) == [3,4] as Set
+        assert (b & a) == [3,4] as Set
+        assert (a & c) == [] as Set
+        assert (b & c) == [5,6] as Set
+        assert (empty & a) == empty
+        assert (a & empty) == empty
+
+        Set d = ['a', 'B', 'c'] as Set
+        SortedSet e = ['A', 'b', 'D'] as SortedSet
+        assert ['a', 'B'] as Set == d.and(e, String.CASE_INSENSITIVE_ORDER)
+        assert empty == d.and(e, Comparator.naturalOrder())
+
+        assert (a & b) instanceof Set
+        assert (a & c) !instanceof SortedSet
+        assert (d.and(e, String.CASE_INSENSITIVE_ORDER)) instanceof Set
+        assert (d.and(e, Comparator.naturalOrder())) !instanceof SortedSet
+    }
+
+    void testIntersectionForSortedSets() {
+        SortedSet empty = [] as SortedSet
+        SortedSet a = [1,2,3,4] as SortedSet
+        Set b = [3,4,5,6] as Set
+        Set c = [5,6,7,8] as SortedSet
+
+        // NOTE: == has higher precedence than &, so need to use parens to test correct logic
+        assert (a & b) == [3,4] as SortedSet
+        assert (c & a) == [] as SortedSet
+        assert (a & c) == [] as SortedSet
+        assert (c & b) == [5,6] as SortedSet
+        assert (empty & a) == empty
+        assert (a & empty) == empty
+
+        SortedSet d = ['a', 'B', 'c'] as SortedSet
+        Set e = ['A', 'b', 'D'] as Set
+        assert ['a', 'B'] as SortedSet == d.and(e, String.CASE_INSENSITIVE_ORDER)
+        assert empty == d.and(e, Comparator.naturalOrder())
+
+        assert (a & b) instanceof SortedSet
+        assert (a & c) instanceof SortedSet
+        assert (d.and(e, String.CASE_INSENSITIVE_ORDER)) instanceof SortedSet
+        assert (d.and(e, Comparator.naturalOrder())) instanceof SortedSet
+    }
+
+    void testSymmetricDifferenceForSets() {
+        Set empty = [] as Set
+        Set a = [1,2,3,4] as Set
+        Set b = [3,4,5,6] as Set
+        Set c = [5,6,7,8] as SortedSet
+
+        // NOTE: == has higher precedence than ^, so need to use parens to test correct logic
+        assert (a ^ b) == [1,2,5,6] as Set
+        assert (b ^ a) == [1,2,5,6] as Set
+        assert (a ^ c) == [1,2,3,4,5,6,7,8] as Set
+        assert (b ^ c) == [3,4,7,8] as Set
+        assert (empty ^ a) == a
+        assert (a ^ empty) == a
+
+        Set d = ['a', 'B', 'c'] as Set
+        SortedSet e = ['A', 'b', 'D'] as SortedSet
+        assert ['c', 'D'] as Set == d.xor(e, String.CASE_INSENSITIVE_ORDER)
+        assert ['a', 'B', 'c', 'A', 'b', 'D'] as Set == d.xor(e, Comparator.naturalOrder())
+
+        assert (a ^ b) instanceof Set
+        assert (a ^ c) !instanceof SortedSet
+        assert (d.xor(e, String.CASE_INSENSITIVE_ORDER)) instanceof Set
+        assert (d.xor(e, Comparator.naturalOrder())) !instanceof SortedSet
+    }
+
+    void testSymmetricDifferenceForSortedSets() {
+        SortedSet empty = [] as SortedSet
+        SortedSet a = [1,2,3,4] as SortedSet
+        Set b = [3,4,5,6] as Set
+        Set c = [5,6,7,8] as SortedSet
+
+        // NOTE: == has higher precedence than ^, so need to use parens to test correct logic
+        assert (a ^ b) == [1,2,5,6] as SortedSet
+        assert (c ^ a) == [1,2,3,4,5,6,7,8] as SortedSet
+        assert (a ^ c) == [1,2,3,4,5,6,7,8] as SortedSet
+        assert (c ^ b) == [3,4,7,8] as SortedSet
+        assert (empty ^ a) == a
+        assert (a ^ empty) == a
+
+        SortedSet d = ['a', 'B', 'c'] as SortedSet
+        Set e = ['A', 'b', 'D'] as Set
+        assert ['c', 'D'] as SortedSet == d.xor(e, String.CASE_INSENSITIVE_ORDER)
+        assert ['a', 'B', 'c', 'A', 'b', 'D'] as SortedSet == d.xor(e, Comparator.naturalOrder())
+
+        assert (a ^ b) instanceof SortedSet
+        assert (a ^ c) instanceof SortedSet
+        assert (d.xor(e, String.CASE_INSENSITIVE_ORDER)) instanceof SortedSet
+        assert (d.xor(e, Comparator.naturalOrder())) instanceof SortedSet
+    }
+
     void testSwap() {
         assert [1, 2, 3, 4] == [1, 2, 3, 4].swap(0, 0).swap(1, 1)