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 2026/07/10 20:02:53 UTC

(commons-collections) branch master updated: Refactor summing sizes for duplication

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 572f9be73 Refactor summing sizes for duplication
572f9be73 is described below

commit 572f9be73d743cc3d360581f18680859c6a45b96
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Jul 10 15:32:55 2026 -0400

    Refactor summing sizes for duplication
    
    - Fail faster
    - Add IterableUtils.sumSizesToInt(Iterable<? extends Collection<E>>)
---
 src/changes/changes.xml                            |  1 +
 .../apache/commons/collections4/IterableUtils.java | 39 ++++++++++++++++++++++
 .../collection/CompositeCollection.java            |  7 ++--
 .../multimap/AbstractMultiValuedMap.java           |  7 ++--
 .../collections4/multiset/AbstractMultiSet.java    | 16 +++++----
 .../commons/collections4/set/CompositeSet.java     |  7 ++--
 .../multimap/AbstractMultiValuedMapTest.java       |  1 +
 7 files changed, 57 insertions(+), 21 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d4ddb86b7..593148abe 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -85,6 +85,7 @@
     <action type="add" dev="ggregory" due-to="Gary Gregory">Add a Maven benchmark profile for JMH.</action>
     <action type="add" issue="COLLECTIONS-893" dev="paulk" due-to="Paul King">Add SortedMultiSet interface, TreeMultiSet implementation, sorted multiset decorators, and MultiSetUtils factory methods, giving MultiSet feature parity with SortedBag/TreeBag.</action>
     <action type="add" issue="COLLECTIONS-893" dev="paulk" due-to="Paul King">Add MultiSetUtils.containsOccurrences/removeOccurrences/retainOccurrences providing Bag's cardinality-respecting semantics under explicit names, and Bag-to-MultiSet migration notes in the Bag javadoc.</action>
+    <action type="add" dev="ggregory" due-to="Gary Gregory">Add IterableUtils.sumSizes(Iterable&lt;? extends Collection&lt;E&gt;&gt;).</action>
     <!-- UPDATE -->
     <action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 81 to 102 #612, #645, #662, #663.</action>
     <action type="update" dev="ggregory" due-to="Gary Gregory">Bump com.google.guava:guava-testlib from 33.3.1-jre to 33.5.0-jre #644.</action>
diff --git a/src/main/java/org/apache/commons/collections4/IterableUtils.java b/src/main/java/org/apache/commons/collections4/IterableUtils.java
index 133b8a93e..4475ffff6 100644
--- a/src/main/java/org/apache/commons/collections4/IterableUtils.java
+++ b/src/main/java/org/apache/commons/collections4/IterableUtils.java
@@ -26,6 +26,7 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.ToIntFunction;
 
 import org.apache.commons.collections4.functors.EqualPredicate;
 import org.apache.commons.collections4.functors.NullPredicate;
@@ -927,6 +928,44 @@ public class IterableUtils {
         };
     }
 
+    /**
+     * Returns the sum of the sizes of the collections in the given iterable.
+     * <p>
+     * Integer overflow is capped at {@link Integer#MAX_VALUE}.
+     * </p>
+     *
+     * @param <E>  The element type of the collections in the iterable.
+     * @param iterable the iterable of collections to sum the sizes of, must not be null.
+     * @return the sum of the sizes of the collections in the iterable, capped at {@link Integer#MAX_VALUE}.
+     * @since 4.6.0
+     */
+    public static <E> int sumSizesToInt(final Iterable<? extends Collection<E>> iterable) {
+        return sumToInt(iterable, Collection::size);
+    }
+
+    /**
+     * Returns the sum of the integer values produced by applying the given function to each element in the iterable.
+     * <p>
+     * Integer overflow is capped at {@link Integer#MAX_VALUE}.
+     * </p>
+     *
+     * @param <C>           The type of the elements in the iterable.
+     * @param iterable      The iterable of elements to sum the integer values of, must not be null.
+     * @param toIntFunction The function to apply to each element to produce an integer value, must not be null.
+     * @return the sum of the integer values produced by applying the function to each element in the iterable, capped at {@link Integer#MAX_VALUE}.
+     */
+    private static <C extends Collection<?>> int sumToInt(final Iterable<C> iterable, final ToIntFunction<C> toIntFunction) {
+        int size = 0;
+        try {
+            for (final C item : iterable) {
+                size = Math.addExact(size, toIntFunction.applyAsInt(item));
+            }
+        } catch (final ArithmeticException e) {
+            size = Integer.MAX_VALUE;
+        }
+        return size;
+    }
+
     /**
      * Gets a new list with the contents of the provided iterable.
      *
diff --git a/src/main/java/org/apache/commons/collections4/collection/CompositeCollection.java b/src/main/java/org/apache/commons/collections4/collection/CompositeCollection.java
index 486e132b1..85cc3baa2 100644
--- a/src/main/java/org/apache/commons/collections4/collection/CompositeCollection.java
+++ b/src/main/java/org/apache/commons/collections4/collection/CompositeCollection.java
@@ -27,6 +27,7 @@ import java.util.function.Predicate;
 import java.util.stream.Stream;
 
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.iterators.EmptyIterator;
 import org.apache.commons.collections4.iterators.IteratorChain;
 import org.apache.commons.collections4.list.UnmodifiableList;
@@ -430,11 +431,7 @@ public class CompositeCollection<E> implements Collection<E>, Serializable {
      */
     @Override
     public int size() {
-        long size = 0;
-        for (final Collection<E> item : all) {
-            size += item.size();
-        }
-        return (int) Math.min(size, Integer.MAX_VALUE);
+        return IterableUtils.sumSizesToInt(all);
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java b/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
index 373f68716..cf8094f07 100644
--- a/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
+++ b/src/main/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMap.java
@@ -31,6 +31,7 @@ import java.util.Objects;
 import java.util.Set;
 
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.IteratorUtils;
 import org.apache.commons.collections4.MapIterator;
 import org.apache.commons.collections4.MultiSet;
@@ -903,11 +904,7 @@ public abstract class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K,
         // but this requires that all modifications of the multimap
         // (including the wrapped collections and entry/value
         // collections) are tracked.
-        long size = 0;
-        for (final Collection<V> col : getMap().values()) {
-            size += col.size();
-        }
-        return (int) Math.min(size, Integer.MAX_VALUE);
+        return IterableUtils.sumSizesToInt(getMap().values());
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java b/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java
index 7de3788a5..ba6b739b9 100644
--- a/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java
+++ b/src/main/java/org/apache/commons/collections4/multiset/AbstractMultiSet.java
@@ -475,16 +475,20 @@ public abstract class AbstractMultiSet<E> extends AbstractCollection<E> implemen
     /**
      * Returns the number of elements in this multiset.
      *
-     * @return current size of the multiset, or {@code Integer.MAX_VALUE}
-     *         if the total exceeds it
+     * @return current size of the multiset, or {@code Integer.MAX_VALUE} if the total exceeds it.
      */
     @Override
     public int size() {
-        long totalSize = 0;
-        for (final Entry<E> entry : entrySet()) {
-            totalSize += entry.getCount();
+        // TODO reuse IterableUtils.sum(Iterable, ToIntFunction)
+        int size = 0;
+        try {
+            for (final Entry<E> entry : entrySet()) {
+                size = Math.addExact(size, entry.getCount());
+            }
+        } catch (final ArithmeticException e) {
+            size = Integer.MAX_VALUE;
         }
-        return (int) Math.min(totalSize, Integer.MAX_VALUE);
+        return size;
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/collections4/set/CompositeSet.java b/src/main/java/org/apache/commons/collections4/set/CompositeSet.java
index 290606e29..5b3bed7bd 100644
--- a/src/main/java/org/apache/commons/collections4/set/CompositeSet.java
+++ b/src/main/java/org/apache/commons/collections4/set/CompositeSet.java
@@ -28,6 +28,7 @@ import java.util.Set;
 import java.util.function.Predicate;
 
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
 import org.apache.commons.collections4.collection.CompositeCollection;
 import org.apache.commons.collections4.iterators.EmptyIterator;
 import org.apache.commons.collections4.iterators.IteratorChain;
@@ -470,11 +471,7 @@ public class CompositeSet<E> implements Set<E>, Serializable {
      */
     @Override
     public int size() {
-        long size = 0;
-        for (final Set<E> item : all) {
-            size += item.size();
-        }
-        return (int) Math.min(size, Integer.MAX_VALUE);
+        return IterableUtils.sumSizesToInt(all);
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java b/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
index 46588a3d1..ed9663541 100644
--- a/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
+++ b/src/test/java/org/apache/commons/collections4/multimap/AbstractMultiValuedMapTest.java
@@ -1293,6 +1293,7 @@ public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTes
         map.put("k2", "v2");
         ((HashBag<String>) map.getMap().get("k1")).add("v1", Integer.MAX_VALUE - 1);
         ((HashBag<String>) map.getMap().get("k2")).add("v2", Integer.MAX_VALUE - 1);
+        ((HashBag<String>) map.getMap().get("k1")).add("v1", Integer.MAX_VALUE - 1);
         assertEquals(Integer.MAX_VALUE, map.size());
     }