[
https://issues.apache.org/jira/browse/COLLECTIONS-891?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Ruiqi Dong updated COLLECTIONS-891:
-----------------------------------
Description:
*Summary*
`IndexedCollection` is still a `Collection`, so `contains( x )` should answer
whether an equal object is present in the collection.
The current implementation answers a different question: whether the collection
contains *any* element whose transformed key matches `keyTransformer.apply( x
)`.
That produces false positives whenever two different objects map to the same
key.
*Affected code*
File:
`src/main/java/org/apache/commons/collections4/collection/IndexedCollection.java`
{code:java}
@SuppressWarnings("unchecked")
@Override
public boolean contains(final Object object) {
return index.containsKey(keyTransformer.apply((C) object));
} {code}
*Reproducer*
Add the following test to
`src/test/java/org/apache/commons/collections4/collection/IndexedCollectionTest.java`:
{code:java}
@Test
void testContainsUsesObjectEqualityNotOnlyTransformedKey() {
final Collection<String> coll = makeUniqueTestCollection();
coll.add("01");
assertFalse(coll.contains("1"));
} {code}
This uses the existing `IntegerTransformer`, so both `"01"` and `"1"` map to
the same key `1`, but the objects are not equal.
Run:
{code:java}
mvn -q
-Dtest=org.apache.commons.collections4.collection.IndexedCollectionTest#testContainsUsesObjectEqualityNotOnlyTransformedKey
test {code}
*Observed behavior*
{code:java}
expected: <false> but was: <true> {code}
*Expected behavior*
`contains("1")` should be `false`, because only `"01"` was added to the
collection.
The class is a `Collection` decorator, not just a key index. Its `contains`
method must preserve collection membership semantics. Matching on transformed
keys instead of object equality breaks the `Collection.contains` contract
whenever the transformer is not injective.
was:
*Summary*
`IndexedCollection` is still a `Collection`, so contains(x) should answer
whether an equal object is present in the collection.
The current implementation answers a different question: whether the collection
contains *any* element whose transformed key matches `keyTransformer.apply(x)`.
That produces false positives whenever two different objects map to the same
key.
*Affected code*
File:
`src/main/java/org/apache/commons/collections4/collection/IndexedCollection.java`
{code:java}
@SuppressWarnings("unchecked")
@Override
public boolean contains(final Object object) {
return index.containsKey(keyTransformer.apply((C) object));
} {code}
*Reproducer*
Add the following test to
`src/test/java/org/apache/commons/collections4/collection/IndexedCollectionTest.java`:
{code:java}
@Test
void testContainsUsesObjectEqualityNotOnlyTransformedKey() {
final Collection<String> coll = makeUniqueTestCollection();
coll.add("01");
assertFalse(coll.contains("1"));
} {code}
This uses the existing `IntegerTransformer`, so both `"01"` and `"1"` map to
the same key `1`, but the objects are not equal.
Run:
{code:java}
mvn -q
-Dtest=org.apache.commons.collections4.collection.IndexedCollectionTest#testContainsUsesObjectEqualityNotOnlyTransformedKey
test {code}
*Observed behavior*
{code:java}
expected: <false> but was: <true> {code}
*Expected behavior*
`contains("1")` should be `false`, because only `"01"` was added to the
collection.
The class is a `Collection` decorator, not just a key index. Its `contains`
method must preserve collection membership semantics. Matching on transformed
keys instead of object equality breaks the `Collection.contains` contract
whenever the transformer is not injective.
> IndexedCollection.contains() can return true for objects that were never added
> ------------------------------------------------------------------------------
>
> Key: COLLECTIONS-891
> URL: https://issues.apache.org/jira/browse/COLLECTIONS-891
> Project: Commons Collections
> Issue Type: Bug
> Components: Collection
> Reporter: Ruiqi Dong
> Priority: Minor
>
> *Summary*
> `IndexedCollection` is still a `Collection`, so `contains( x )` should answer
> whether an equal object is present in the collection.
>
> The current implementation answers a different question: whether the
> collection contains *any* element whose transformed key matches
> `keyTransformer.apply( x )`.
> That produces false positives whenever two different objects map to the same
> key.
>
> *Affected code*
> File:
> `src/main/java/org/apache/commons/collections4/collection/IndexedCollection.java`
> {code:java}
> @SuppressWarnings("unchecked")
> @Override
> public boolean contains(final Object object) {
> return index.containsKey(keyTransformer.apply((C) object));
> } {code}
> *Reproducer*
> Add the following test to
> `src/test/java/org/apache/commons/collections4/collection/IndexedCollectionTest.java`:
> {code:java}
> @Test
> void testContainsUsesObjectEqualityNotOnlyTransformedKey() {
> final Collection<String> coll = makeUniqueTestCollection();
> coll.add("01");
> assertFalse(coll.contains("1"));
> } {code}
> This uses the existing `IntegerTransformer`, so both `"01"` and `"1"` map to
> the same key `1`, but the objects are not equal.
>
> Run:
> {code:java}
> mvn -q
> -Dtest=org.apache.commons.collections4.collection.IndexedCollectionTest#testContainsUsesObjectEqualityNotOnlyTransformedKey
> test {code}
> *Observed behavior*
> {code:java}
> expected: <false> but was: <true> {code}
> *Expected behavior*
> `contains("1")` should be `false`, because only `"01"` was added to the
> collection.
>
> The class is a `Collection` decorator, not just a key index. Its `contains`
> method must preserve collection membership semantics. Matching on transformed
> keys instead of object equality breaks the `Collection.contains` contract
> whenever the transformer is not injective.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)