From 8916770c8c02d3217546d96ae815e6ba0dbedfa4 Mon Sep 17 00:00:00 2001 From: OldTruckDriver Date: Sat, 20 Jun 2026 00:02:53 +1000 Subject: [PATCH] [COLLECTIONS-891] Fix IndexedCollection contains equality contains(Object) returned index.containsKey(transform(object)), so it reported true whenever any element shared the transformed key, breaking Collection.contains for a non-injective transformer. Look up the key's bucket and check actual object equality within it. Add tests for unique and non-unique indexes. Reviewed-by: OpenAI Codex Reviewed-by: Anthropic Claude Code --- src/changes/changes.xml | 1 + .../collection/IndexedCollection.java | 3 ++- .../collection/IndexedCollectionTest.java | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d631a8bc57..169f2f5f3b 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -52,6 +52,7 @@ Re-validate entries in PredicatedMap/PredicatedCollection readObject (#682). IndexedCollection.remove(Object) removes all values for a non-unique index key. TransformIterator throws NullPointerException when its transformer is null. + IndexedCollection.contains(Object) can return true for objects that were never added. MapUtils.invertMap() improves HashMap construction (#652). Validate order and uniqueness invariants in decorator readObject() (#684). diff --git a/src/main/java/org/apache/commons/collections4/collection/IndexedCollection.java b/src/main/java/org/apache/commons/collections4/collection/IndexedCollection.java index c534703a37..17882daabf 100644 --- a/src/main/java/org/apache/commons/collections4/collection/IndexedCollection.java +++ b/src/main/java/org/apache/commons/collections4/collection/IndexedCollection.java @@ -159,7 +159,8 @@ public void clear() { @SuppressWarnings("unchecked") @Override public boolean contains(final Object object) { - return index.containsKey(keyTransformer.apply((C) object)); + final Collection values = (Collection) index.get(keyTransformer.apply((C) object)); + return values != null && values.contains(object); } /** diff --git a/src/test/java/org/apache/commons/collections4/collection/IndexedCollectionTest.java b/src/test/java/org/apache/commons/collections4/collection/IndexedCollectionTest.java index db427309dd..b608d1b4f6 100644 --- a/src/test/java/org/apache/commons/collections4/collection/IndexedCollectionTest.java +++ b/src/test/java/org/apache/commons/collections4/collection/IndexedCollectionTest.java @@ -18,6 +18,7 @@ import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -116,6 +117,22 @@ void testAddedObjectsCanBeRetrievedByKey() throws Exception { assertEquals("4", indexed.get(4)); } + @Test + void testContainsUsesObjectEqualityNotOnlyTransformedKey() { + final Collection coll = makeUniqueTestCollection(); + coll.add("01"); + + assertFalse(coll.contains("1")); + } + + @Test + void testContainsUsesObjectEqualityWithNonUniqueIndex() { + final Collection coll = makeTestCollection(); + coll.add("01"); + + assertFalse(coll.contains("1")); + } + @Test void testDecoratedCollectionIsIndexedOnCreation() throws Exception { final Collection original = makeFullCollection();