You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Ruiqi Dong (Jira)" <ji...@apache.org> on 2026/05/21 10:14:00 UTC

[jira] [Created] (MATH-1689) Frequency.equals()/hashCode() ignores the comparator

Ruiqi Dong created MATH-1689:
--------------------------------

             Summary: Frequency.equals()/hashCode() ignores the comparator
                 Key: MATH-1689
                 URL: https://issues.apache.org/jira/browse/MATH-1689
             Project: Commons Math
          Issue Type: Bug
          Components: legacy
            Reporter: Ruiqi Dong


*Summary*
Frequency behavior depends on the comparator used by its internal map, but equals() and hashCode() ignore that comparator. Two Frequency objects with different ordering semantics can therefore compare equal.
*Affected code*
File: commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/stat/Frequency.java
{code:java}
public long getCumFreq(T v) {
    ...
    Comparator<? super T> c = freqTable.comparator();
    if (c == null) {
        c = new NaturalComparator<>();
    }
    ...
}

public List<T> getMode() {
    ...
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((freqTable == null) ? 0 : freqTable.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    ...
    Frequency<?> other = (Frequency<?>) obj;
    return freqTable.equals(other.freqTable);
} {code}
 
*Reproducer*
Add the following test to commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/stat/FrequencyTest.java:
{code:java}
@Test
public void testEqualsShouldConsiderComparator() {
    Frequency<Integer> ascending = new Frequency<>();
    Frequency<Integer> descending = new Frequency<Integer>(Comparator.reverseOrder());
    ascending.addValue(1);
    ascending.addValue(2);
    descending.addValue(1);
    descending.addValue(2);

    Assert.assertEquals(1, ascending.getCumFreq(1));
    Assert.assertEquals(2, descending.getCumFreq(1));
    Assert.assertEquals("[1, 2]", ascending.getMode().toString());
    Assert.assertEquals("[2, 1]", descending.getMode().toString());
    Assert.assertNotEquals(ascending, descending);
} {code}
Run:
{code:java}
mvn -q -pl commons-math-legacy -Dtest=org.apache.commons.math4.legacy.stat.FrequencyTest test {code}
Observed behavior:
{code:java}
FrequencyTest.testEqualsShouldConsiderComparator:223
Values should be different. Actual:
Value 	 Freq. 	 Pct. 	 Cum Pct.
2	1	50%	50%
1	1	50%	100% {code}
Expected behavior:
Frequencies with different comparators should not compare equal when their observable ordering behavior differs.
 
The comparator affects at least getCumFreq() and getMode(). Since those outputs differ, equality and hashing should not ignore the comparator.
 



--
This message was sent by Atlassian Jira
(v8.20.10#820010)