From 5a61bbe39e5a9bbde63ad4fb6e85c74121424539 Mon Sep 17 00:00:00 2001 From: Nishant Mehta Date: Fri, 26 Jun 2026 13:49:05 -0400 Subject: [PATCH 1/2] Avoid allocations in IterableUtils.frequency for the general case frequency() handled non-Set, non-Bag iterables by building a filtered- iterable pipeline: size(filteredIterable(emptyIfNull(iterable), EqualPredicate.equalPredicate(obj))) which allocates an EqualPredicate, a FluentIterable decorator and a filtered iterator on every call just to count matching elements. Since this also backs CollectionUtils.cardinality(), the cost is paid by a commonly used utility. Count directly with a single pass instead. EqualPredicate.equalPredicate(obj) evaluates Objects.equals(obj, element) (and equalPredicate(null) matches null elements), so the loop reproduces the previous semantics exactly, including null handling. The direct loop also lets the JIT scalar-replace the iterator. Measured with a ThreadMXBean allocation driver (200k warmed ops): CollectionUtils.cardinality 52 B/op -> 0 B/op. CollectionUtilsTest (161) and IterableUtilsTest (42) pass unchanged. Signed-off-by: Nishant Mehta --- .../apache/commons/collections4/IterableUtils.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/collections4/IterableUtils.java b/src/main/java/org/apache/commons/collections4/IterableUtils.java index 5a06d3fb08..4fecffa226 100644 --- a/src/main/java/org/apache/commons/collections4/IterableUtils.java +++ b/src/main/java/org/apache/commons/collections4/IterableUtils.java @@ -543,7 +543,16 @@ public static int frequency(final Iterable iterable, final T if (iterable instanceof Bag) { return ((Bag) iterable).getCount(obj); } - return size(filteredIterable(emptyIfNull(iterable), EqualPredicate.equalPredicate(obj))); + if (iterable == null) { + return 0; + } + int count = 0; + for (final E element : iterable) { + if (Objects.equals(obj, element)) { + count++; + } + } + return count; } /** From 80a5610dfa598768ce3c370fc296086811023a92 Mon Sep 17 00:00:00 2001 From: Nishant Mehta Date: Fri, 26 Jun 2026 13:51:22 -0400 Subject: [PATCH 2/2] Avoid allocations in IterableUtils.countMatches countMatches() built a filtered-iterable pipeline just to count elements: size(filteredIterable(emptyIfNull(input), predicate)) allocating a FluentIterable decorator and a filtered iterator on every call. Count directly in a single pass instead. FilterIterator advances using predicate.test(element), so calling predicate.test in the loop reproduces the previous matching semantics, and emptyIfNull's null handling becomes an explicit null check. Measured with a ThreadMXBean allocation driver (200k warmed ops): IterableUtils.countMatches 66 B/op -> 0 B/op. CollectionUtilsTest (161) and IterableUtilsTest (42) pass unchanged. Signed-off-by: Nishant Mehta --- .../org/apache/commons/collections4/IterableUtils.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/collections4/IterableUtils.java b/src/main/java/org/apache/commons/collections4/IterableUtils.java index 4fecffa226..378d199d9b 100644 --- a/src/main/java/org/apache/commons/collections4/IterableUtils.java +++ b/src/main/java/org/apache/commons/collections4/IterableUtils.java @@ -341,7 +341,15 @@ public static boolean contains(final Iterable iterable, final Object obje */ public static long countMatches(final Iterable input, final Predicate predicate) { Objects.requireNonNull(predicate, "predicate"); - return size(filteredIterable(emptyIfNull(input), predicate)); + long count = 0; + if (input != null) { + for (final E element : input) { + if (predicate.test(element)) { + count++; + } + } + } + return count; } /**