You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2024/09/01 15:01:57 UTC

(commons-collections) 02/02: Add CollectionUtils.duplicate[List|Set]

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

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-collections.git

commit 77faa5be8f9a35b2757271f934d85729714e017d
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Sep 1 11:01:45 2024 -0400

    Add CollectionUtils.duplicate[List|Set]
    
    - Add CollectionUtils.duplicateList(Collection<E>)
    - Add CollectionUtils.duplicateSequencedSet(Collection<E>)
    - Add CollectionUtils.duplicateSet(Collection<E>)
---
 src/changes/changes.xml                            |   3 +
 .../commons/collections4/CollectionUtils.java      |  57 +++++++++
 .../commons/collections4/CollectionUtilsTest.java  | 134 +++++++++++++++++++++
 3 files changed, 194 insertions(+)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index a258e8228..9abcc6b30 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -37,6 +37,9 @@
     <action type="fix" dev="ggregory" due-to="Dávid Szigecsán">Use the Junit (Jupiter) API #518.</action>
     <!-- ADD -->
     <action type="add" dev="ggregory" due-to="Dávid Szigecsán">LayerManager.Builder implements Supplier.</action>
+    <action type="add" dev="ggregory" due-to="Gary Gregory, hemanth0525">Add CollectionUtils.duplicateList(Collection).</action>
+    <action type="add" dev="ggregory" due-to="Gary Gregory, hemanth0525">Add CollectionUtils.duplicateSet(Collection).</action>
+    <action type="add" dev="ggregory" due-to="Gary Gregory, hemanth0525">Add CollectionUtils.duplicateSequencedSet(Collection).</action>
     <!-- UPDATE -->
     <action issue="COLLECTIONS-857" type="update" dev="ggregory" due-to="Claude Warren">Update bloom filter documentation #508.</action>
     <action issue="COLLECTIONS-857" type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump commons-codec:commons-codec from 1.17.0 to 1.17.1 #514.</action>
diff --git a/src/main/java/org/apache/commons/collections4/CollectionUtils.java b/src/main/java/org/apache/commons/collections4/CollectionUtils.java
index d6d8d342e..cf7d7c984 100644
--- a/src/main/java/org/apache/commons/collections4/CollectionUtils.java
+++ b/src/main/java/org/apache/commons/collections4/CollectionUtils.java
@@ -25,6 +25,7 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
@@ -756,6 +757,62 @@ public class CollectionUtils {
         return helper.list();
     }
 
+    /**
+     * Finds and returns the List of duplicate elements in the given collection.
+     *
+     * @param <E> the type of elements in the collection.
+     * @param collection the list to test, must not be null.
+     * @return the set of duplicate elements, may be empty.
+     * @since 4.5.0-M3
+     */
+    public static <E> List<E> duplicateList(final Collection<E> collection) {
+        return new ArrayList<>(duplicateSet(collection));
+    }
+
+    /**
+     * Finds and returns the sequenced Set of duplicate elements in the given collection.
+     * <p>
+     * Once we are on Java 21 and a new major version, the return type should be SequencedSet.
+     * </p>
+     *
+     * @param <E> the type of elements in the collection.
+     * @param collection the list to test, must not be null.
+     * @return the set of duplicate elements, may be empty.
+     * @since 4.5.0-M3
+     */
+    public static <E> Set<E> duplicateSequencedSet(final Collection<E> collection) {
+        return duplicateSet(collection, new LinkedHashSet<>());
+    }
+
+    /**
+     * Finds and returns the set of duplicate elements in the given collection.
+     *
+     * @param <E> the type of elements in the collection.
+     * @param collection the list to test, must not be null.
+     * @return the set of duplicate elements, may be empty.
+     * @since 4.5.0-M3
+     */
+    public static <E> Set<E> duplicateSet(final Collection<E> collection) {
+        return duplicateSet(collection, new HashSet<>());
+    }
+
+    /**
+     * Worker method for {@link #duplicateSet(Collection)} and friends.
+     *
+     * @param <C> the type of Collection.
+     * @param <E> the type of elements in the Collection.
+     * @param collection the list to test, must not be null.
+     * @param duplicates the list to test, must not be null.
+     * @return the set of duplicate elements, may be empty.
+     */
+    static <C extends Collection<E>, E> C duplicateSet(final Collection<E> collection, final C duplicates) {
+        final Set<E> set = new HashSet<>();
+        for (final E e : collection) {
+            (set.contains(e) ? duplicates : set).add(e);
+        }
+        return duplicates;
+    }
+
     /**
      * Returns the immutable EMPTY_COLLECTION with generic type safety.
      *
diff --git a/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java b/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
index aaecfa991..811396067 100644
--- a/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
+++ b/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java
@@ -26,17 +26,20 @@ import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Deque;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -872,6 +875,137 @@ public class CollectionUtilsTest extends MockTestCase {
         assertThrows(NullPointerException.class, () -> CollectionUtils.disjunction(list, null));
     }
 
+    @Test
+    public void testDuplicateListAllSameInList() {
+        final List<Integer> input = Arrays.asList(5, 5, 5, 5);
+        assertEquals(Arrays.asList(5), CollectionUtils.duplicateList(input));
+    }
+
+    @Test
+    public void testDuplicateListEmptyDeque() {
+        assertTrue(CollectionUtils.duplicateList(new ArrayDeque<>()).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateListEmptyList() {
+        final List<Integer> input = Arrays.asList();
+        assertTrue(CollectionUtils.duplicateList(input).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateListEmptySet() {
+        assertTrue(CollectionUtils.duplicateList(new HashSet<>()).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateListMultipleDuplicatesInDeque() {
+        final Deque<Integer> input = new ArrayDeque<>(Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4));
+        final List<Integer> expected = Arrays.asList(1, 2, 3, 4);
+        assertEquals(expected, CollectionUtils.duplicateList(input));
+    }
+
+    @Test
+    public void testDuplicateListMultipleDuplicatesInList() {
+        final List<Integer> input = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4);
+        final List<Integer> expected = Arrays.asList(1, 2, 3, 4);
+        assertEquals(expected, CollectionUtils.duplicateList(input));
+    }
+
+    @Test
+    public void testDuplicateListNoDuplicates() {
+        final List<Integer> input = Arrays.asList(1, 2, 3, 4, 5);
+        assertTrue(CollectionUtils.duplicateList(input).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateListSingleElement() {
+        final List<Integer> input = Arrays.asList(1);
+        assertTrue(CollectionUtils.duplicateList(input).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateListWithDuplicates() {
+        final List<Integer> input = Arrays.asList(1, 2, 3, 2, 4, 5, 3);
+        final List<Integer> expected = Arrays.asList(2, 3);
+        assertEquals(expected, CollectionUtils.duplicateList(input));
+    }
+
+    @Test
+    public void testDuplicateSequencedSetMultipleDuplicates() {
+        final List<Integer> input = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4);
+        final List<Integer> list = Arrays.asList(1, 2, 3, 4);
+        assertEquals(list, new ArrayList<>(CollectionUtils.duplicateSequencedSet(input)));
+        assertEquals(new LinkedHashSet<>(list), CollectionUtils.duplicateSequencedSet(input));
+    }
+
+    @Test
+    public void testDuplicateSetEmptyDeque() {
+        assertTrue(CollectionUtils.duplicateSet(new ArrayDeque<>()).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateSetEmptyList() {
+        final List<Integer> input = Arrays.asList();
+        assertTrue(CollectionUtils.duplicateSet(input).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateSetEmptySet() {
+        assertTrue(CollectionUtils.duplicateSet(new HashSet<>()).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateSetInSet() {
+        // Sets don't have duplicates, so the result is always an empty set.
+        final Set<Integer> input = new HashSet<>(Arrays.asList(5));
+        assertTrue(CollectionUtils.duplicateSet(input).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateSetMultipleDuplicatesInDeque() {
+        final Deque<Integer> input = new ArrayDeque<>(Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4));
+        final Set<Integer> expected = new HashSet<>(Arrays.asList(1, 2, 3, 4));
+        assertEquals(expected, CollectionUtils.duplicateSet(input));
+    }
+
+    @Test
+    public void testDuplicateSetMultipleDuplicatesInList() {
+        final List<Integer> input = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4);
+        final Set<Integer> expected = new HashSet<>(Arrays.asList(1, 2, 3, 4));
+        assertEquals(expected, CollectionUtils.duplicateSet(input));
+    }
+
+    @Test
+    public void testDuplicateSetNoDuplicates() {
+        final List<Integer> input = Arrays.asList(1, 2, 3, 4, 5);
+        assertTrue(CollectionUtils.duplicateSet(input).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateSetSingleElement() {
+        final List<Integer> input = Arrays.asList(1);
+        assertTrue(CollectionUtils.duplicateSet(input).isEmpty());
+    }
+
+    @Test
+    public void testDuplicateSetWithDuplicates() {
+        final List<Integer> input = Arrays.asList(1, 2, 3, 2, 4, 5, 3);
+        final Set<Integer> expected = new HashSet<>(Arrays.asList(2, 3));
+        assertEquals(expected, CollectionUtils.duplicateSet(input));
+    }
+
+    @Test
+    public void testDuplicatListAllSameInDeque() {
+        final Deque<Integer> input = new ArrayDeque<>(Arrays.asList(5, 5, 5, 5));
+        assertEquals(Arrays.asList(5), CollectionUtils.duplicateList(input));
+    }
+
+    @Test
+    public void testDuplicatSetAllSameInDeque() {
+        final Deque<Integer> input = new ArrayDeque<>(Arrays.asList(5, 5, 5, 5));
+        assertEquals(new HashSet<>(Arrays.asList(5)), CollectionUtils.duplicateSet(input));
+    }
+
     @Test
     public void testEmptyCollection() throws Exception {
         final Collection<Number> coll = CollectionUtils.emptyCollection();