Repository: yegor256/cactoos
Pull request #1782 (2025-03-17 15:06:46)

Add Union class for set operations
This PR adds a new `Union` class to the  package that represents the union of two sets. The implementation follows the same pattern as the existing Diff class.
The Union class:
Extends `SetEnvelope<T>`
Provides constructors for various input types (iterables, iterators, scalar suppliers)
Includes a private static method to compute the union of two sets
Is fully tested with various scenarios including empty sets, Java's built-in sets, and collections
This implementation complements the existing set operations in the library and follows the same design principles.


Patch:
diff --git a/src/main/java/org/cactoos/set/Union.java b/src/main/java/org/cactoos/set/Union.java
new file mode 100644
index 00000000..13a56f58
--- /dev/null
+++ b/src/main/java/org/cactoos/set/Union.java
@@ -0,0 +1,87 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
+ * SPDX-License-Identifier: MIT
+ */
+package org.cactoos.set;
+
+import java.util.Iterator;
+import org.cactoos.Scalar;
+import org.cactoos.iterable.IterableOf;
+import org.cactoos.scalar.Unchecked;
+
+/**
+ * Set union.
+ *
+ * <p>There is no thread-safety guarantee.</p>
+ *
+ * @param <T> Type of item
+ * @since 0.58.0
+ */
+public final class Union<T> extends SetEnvelope<T> {
+
+    /**
+     * Ctor.
+     * @param first First set
+     * @param second Second set
+     */
+    public Union(final Iterable<T> first, final Iterable<T> second) {
+        super(
+            computeUnion(
+                new SetOf<>(first),
+                new SetOf<>(second)
+            )
+        );
+    }
+
+    /**
+     * Ctor.
+     * @param first First iterator
+     * @param second Second iterator
+     */
+    public Union(final Iterator<T> first, final Iterator<T> second) {
+        this(
+            new IterableOf<>(first),
+            new IterableOf<>(second)
+        );
+    }
+
+    /**
+     * Ctor.
+     * @param first First iterable supplier
+     * @param second Second iterable supplier
+     */
+    public Union(
+        final Scalar<Iterable<T>> first,
+        final Scalar<Iterable<T>> second
+    ) {
+        this(
+            new Unchecked<>(first).value(),
+            new Unchecked<>(second).value()
+        );
+    }
+
+    /**
+     * Ctor.
+     * @param first First set
+     * @param second Second set
+     */
+    public Union(final SetOf<T> first, final SetOf<T> second) {
+        super(computeUnion(first, second));
+    }
+
+    /**
+     * Compute the union of two sets.
+     * @param first The first set
+     * @param second The second set
+     * @param <E> Type of elements
+     * @return The union set (elements in either first or second)
+     */
+    private static <E> SetOf<E> computeUnion(
+        final SetOf<E> first,
+        final SetOf<E> second
+    ) {
+        final SetOf<E> result = new SetOf<>(first);
+        result.addAll(second);
+        return result;
+    }
+}


Test patch:
diff --git a/src/test/java/org/cactoos/set/UnionTest.java b/src/test/java/org/cactoos/set/UnionTest.java
new file mode 100644
index 00000000..57da2590
--- /dev/null
+++ b/src/test/java/org/cactoos/set/UnionTest.java
@@ -0,0 +1,131 @@
+/*
+ * SPDX-FileCopyrightText: Copyright (c) 2017-2025 Yegor Bugayenko
+ * SPDX-License-Identifier: MIT
+ */
+package org.cactoos.set;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+import org.llorllale.cactoos.matchers.Assertion;
+import org.llorllale.cactoos.matchers.HasSize;
+import org.llorllale.cactoos.matchers.HasValues;
+
+/**
+ * Test case for {@link Union}.
+ *
+ * @since 0.58.0
+ */
+final class UnionTest {
+
+    /**
+     * Tests that set union can be computed correctly.
+     */
+    @Test
+    void computesSetUnion() {
+        new Assertion<>(
+            "Can't compute the union of two sets",
+            new Union<>(
+                new SetOf<>(1, 2, 3),
+                new SetOf<>(3, 4, 5)
+            ),
+            new HasValues<>(1, 2, 3, 4, 5)
+        ).affirm();
+    }
+
+    /**
+     * Tests that set union with empty second set returns the first set.
+     */
+    @Test
+    void computesSetUnionWithEmptySecondSet() {
+        new Assertion<>(
+            "Can't compute the union with empty second set",
+            new Union<>(
+                new SetOf<>(1, 2, 3),
+                new SetOf<>()
+            ),
+            new HasValues<>(1, 2, 3)
+        ).affirm();
+    }
+
+    /**
+     * Tests that set union with empty first set returns the second set.
+     */
+    @Test
+    void computesSetUnionWithEmptyFirstSet() {
+        new Assertion<>(
+            "Can't compute the union with empty first set",
+            new Union<>(
+                new SetOf<Integer>(),
+                new SetOf<>(1, 2, 3)
+            ),
+            new HasValues<>(1, 2, 3)
+        ).affirm();
+    }
+
+    /**
+     * Tests that set union works with java.util.Set.
+     */
+    @Test
+    void computesSetUnionWithJavaUtilSets() {
+        final Set<Integer> first = new HashSet<>();
+        first.add(1);
+        first.add(2);
+        first.add(3);
+        final Set<Integer> second = new HashSet<>();
+        second.add(3);
+        second.add(4);
+        second.add(5);
+        new Assertion<>(
+            "Can't compute the union of two java.util.Set",
+            new Union<>(first, second),
+            new HasValues<>(1, 2, 3, 4, 5)
+        ).affirm();
+    }
+
+    /**
+     * Tests that set union works with iterables.
+     */
+    @Test
+    void computesSetUnionWithIterables() {
+        new Assertion<>(
+            "Can't compute the union of two iterables",
+            new Union<>(
+                Collections.singletonList(1),
+                Collections.singletonList(2)
+            ),
+            new HasValues<>(1, 2)
+        ).affirm();
+    }
+
+    /**
+     * Tests that set union works with iterators.
+     */
+    @Test
+    void computesSetUnionWithIterators() {
+        new Assertion<>(
+            "Can't compute the union of two iterators",
+            new Union<>(
+                new SetOf<>(1, 2, 3).iterator(),
+                new SetOf<>(3, 4, 5).iterator()
+            ),
+            new HasValues<>(1, 2, 3, 4, 5)
+        ).affirm();
+    }
+
+    /**
+     * Tests that set union with identical sets works correctly.
+     */
+    @Test
+    void computesSetUnionWithIdenticalSets() {
+        new Assertion<>(
+            "Can't compute the union of identical sets",
+            new Union<>(
+                new SetOf<>(1, 2, 3),
+                new SetOf<>(1, 2, 3)
+            ),
+            new HasSize(3)
+        ).affirm();
+    }
+}