Skip to content

fix: key UnorderedPairMap by the pair, not by the pair's hash - #1485

Open
mvanhorn wants to merge 1 commit into
AlmasB:devfrom
mvanhorn:fix/unordered-pair-map-hash-collision
Open

mvanhorn wants to merge 1 commit into
AlmasB:devfrom
mvanhorn:fix/unordered-pair-map-hash-collision

Conversation

@mvanhorn

Copy link
Copy Markdown
Contributor

Summary

Fixes #1484

The map is now keyed by an UnorderedPair that implements equals as well as hashCode, keeping the same order-independent hash, so colliding pairs share a bucket but stay separate entries.

Why

UnorderedPairMap stored values in a HashMap<Int, V> keyed by the combined hash of the two keys, so two distinct pairs whose hashes collided shared one entry. put() overwrote, get() returned the other pair's value, and remove() evicted the other pair. PhysicsWorld keeps active collisions in this map, which is why the collision count came up short intermittently and only on some platforms.

Tests

  • UnorderedPairMapTest gets a regression using a key type with a constant hashCode, so every pair collides, and checks get, values and remove keep pairs distinct.
  • The two PhysicsWorldTest collision assertions now carry a label naming the strategy, so a future failure says which one.

Locally on JDK 25: UnorderedPairMapTest 3/3, PhysicsWorldTest 14/14.

Pull Request (PR) prerequisites

Discussed and confirmed in #1484 before opening. No refactoring beyond the fix and its tests.

UnorderedPairMap stored values in a HashMap<Int, V> keyed by the combined
hash of the two keys. Two distinct pairs whose combined hashes collide
therefore shared one entry: put() overwrote the earlier pair's value, get()
returned the wrong pair's value, and remove() on one pair evicted the other.

PhysicsWorld keeps active collisions in this map, so a hash collision between
two entity pairs silently dropped a real collision and onCollision under-
reported. Identity hash codes vary by JVM, platform, and allocation history,
which is why this surfaced as an intermittent, platform-specific collision
count rather than a deterministic failure.

Key the map by an UnorderedPair that implements equals as well as hashCode,
keeping the existing order-independent hash. Collisions now land in the same
bucket and are separated by equals, as HashMap intends.

Adds a regression with a key type whose hashCode is constant, so every pair
collides, and asserts distinct pairs stay distinct through get, values and
remove. Also labels the two PhysicsWorldTest collision assertions so a future
failure names which strategy and which phase produced the wrong count.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VoGxx6nHtCDdeiQrTEWXFz

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UnorderedPairMap keys by hash, so colliding pairs overwrite each other and PhysicsWorld drops collisions

1 participant