Skip to content

Add equals/hashCode consistent with compareTo for value-semantics Comparable classes - #1201

Merged
dr-jts merged 2 commits into
locationtech:masterfrom
grootstebozewolf:fix-1184-comparable-equals
Jun 17, 2026
Merged

Add equals/hashCode consistent with compareTo for value-semantics Comparable classes#1201
dr-jts merged 2 commits into
locationtech:masterfrom
grootstebozewolf:fix-1184-comparable-equals

Conversation

@grootstebozewolf

Copy link
Copy Markdown
Contributor

Several JTS classes implement Comparable with a meaningful compareTo() but do not override equals(), so two objects that compareTo() orders as equal (returns 0) are not equal by equals(). This violates the recommended consistency between compareTo and equals, and means the affected types misbehave as keys in hash-based collections.

Add equals() and hashCode() to the classes whose compareTo defines a genuine value equality, defining equals() as (compareTo == 0) so the relation holds by construction, with hashCode() over the same fields.

Changes Made:

  • LinearLocation(componentIndex, segmentIndex, segmentFraction).
  • EdgeIntersection(segmentIndex, dist).
  • NodeSection(isA, dim, id, ringId, v0, v1).
  • OrientedCoordinateArray — points walked in canonical orientation order, so an array and its reverse (which compare equal) hash equally.
  • Add consistency tests for each updated class (equals/compareTo agreement, the equals/hashCode contract, and orientation-independence for OrientedCoordinateArray).
  • Update history.

The remaining Comparable classes (BoundablePair, EdgeEnd, Corner, SweepLineEvent, OffsetCurveSection, …) use compareTo as a sort or priority ordering keyed on a subset of state, where compareTo == 0 does not imply object identity (e.g. BoundablePair orders by distance only). Aligning equals there would conflate distinct objects, so they are intentionally left unchanged, as the Comparable contract permits.

Fixes#1184

…parable classes (locationtech#1184)
Several classes implement Comparable with a meaningful compareTo() but do
not override equals(), so two objects ordered as equal (compareTo == 0)
are not equal by equals().
Add equals() and hashCode() to the classes whose compareTo defines a
genuine value equality, defining equals() as (compareTo == 0) so the
relation holds by construction, with hashCode() over the same fields:
- LinearLocation (componentIndex, segmentIndex, segmentFraction)
- EdgeIntersection (segmentIndex, dist)
- NodeSection (isA, dim, id, ringId, v0, v1)
- OrientedCoordinateArray (points, walked in canonical orientation order
so an array and its reverse hash equally)
The remaining Comparable classes (BoundablePair, EdgeEnd, Corner,
SweepLineEvent, OffsetCurveSection, ...) use compareTo as a sort or
priority ordering keyed on a subset of state, where compareTo == 0 does
not imply object identity; aligning equals there would conflate distinct
objects, so they are intentionally left unchanged (as the Comparable
contract permits).
Add consistency tests for the updated classes. Update history.
Fixeslocationtech#1184
Co-authored-by: Claude <noreply@anthropic.com>
@dr-jts
dr-jts merged commit d923a01 into locationtech:masterJun 17, 2026
2 checks passed
bjornharrtell pushed a commit to bjornharrtell/jts that referenced this pull request Aug 31, 2026
…ontech#1228)
compareTo() used raw </> comparisons on segmentFraction/dist, which are
always false for NaN and made compareTo (and thus equals(), added in locationtech#1201)
fall through to 0 whenever NaN was compared to any other value on the same
segment/index. This broke transitivity of equals() and its consistency with
hashCode(). Use Double.compare() instead, matching Double's NaN ordering
semantics already used by hashCode().
bjornharrtell added a commit to bjornharrtell/jts that referenced this pull request Aug 31, 2026
…ontech#1228)
compareTo() used raw </> comparisons on segmentFraction/dist, which are
always false for NaN and made compareTo (and thus equals(), added in locationtech#1201)
fall through to 0 whenever NaN was compared to any other value on the same
segment/index. This broke transitivity of equals() and its consistency with
hashCode(). Use Double.compare() instead, matching Double's NaN ordering
semantics already used by hashCode().
bjornharrtell added a commit to bjornharrtell/jts that referenced this pull request Aug 31, 2026
…ontech#1228)
compareTo() used raw </> comparisons on segmentFraction/dist, which are
always false for NaN and made compareTo (and thus equals(), added in locationtech#1201)
fall through to 0 whenever NaN was compared to any other value on the same
segment/index. This broke transitivity of equals() and its consistency with
hashCode(). Use Double.compare() instead, matching Double's NaN ordering
semantics already used by hashCode().
bjornharrtell added a commit to bjornharrtell/jts that referenced this pull request Aug 31, 2026
compareTo() used raw </> comparisons on segmentFraction/dist, which are
always false for NaN and made compareTo (and thus equals(), added in locationtech#1201)
fall through to 0 whenever NaN was compared to any other value on the same
segment/index. This broke transitivity of equals() and its consistency with
hashCode(). Use Double.compare() instead, matching Double's NaN ordering
semantics already used by hashCode().
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Several Comparable classes do not override equals(), making compareTo inconsistent with equals

2 participants

@grootstebozewolf@dr-jts