Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ package com.almasb.fxgl.core.collection
class UnorderedPairMap<K, V>
@JvmOverloads constructor(capacity: Int = 16) {

private val map = HashMap<Int, V>(capacity)
private val map = HashMap<UnorderedPair<K>, V>(capacity)

val values
get() = map.values
Expand All @@ -30,31 +30,43 @@ class UnorderedPairMap<K, V>
* @return a value for [key1] [key2] pair or null if no such key exists
*/
fun get(key1: K, key2: K): V? {
return map[hash(key1, key2)]
return map[UnorderedPair(key1, key2)]
}

/**
* Add a new mapping from [key1] [key2] to [value].
*/
fun put(key1: K, key2: K, value: V) {
map[hash(key1, key2)] = value
map[UnorderedPair(key1, key2)] = value
}

/**
* Remove an existing mapping whose key is [key1] [key2].
*/
fun remove(key1: K, key2: K) {
map.remove(hash(key1, key2))
map.remove(UnorderedPair(key1, key2))
}

private fun hash(key1: K, key2: K): Int {
val hash1 = key1.hashCode()
val hash2 = key2.hashCode()
/**
* Unordered pair key. Distinct pairs with the same hash must remain distinct.
*/
private class UnorderedPair<K>(val a: K, val b: K) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is UnorderedPair<*>) return false

return (a == other.a && b == other.b) || (a == other.b && b == other.a)
}

override fun hashCode(): Int {
val hash1 = a.hashCode()
val hash2 = b.hashCode()

return if (hash1 > hash2) {
31 * (31 + hash1) + hash2
} else {
31 * (31 + hash2) + hash1
return if (hash1 > hash2) {
31 * (31 + hash1) + hash2
} else {
31 * (31 + hash2) + hash1
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,33 @@ class UnorderedPairMapTest {
assertThat(map2.get(key1, CustomObject()), nullValue())
}

@Test
fun `Different pairs with the same hash stay distinct`() {
class Key(val id: Int) {
override fun hashCode() = 7
override fun equals(other: Any?) = other is Key && id == other.id
}

val map2 = UnorderedPairMap<Key, String>()
val a = Key(1)
val b = Key(2)
val c = Key(3)
val d = Key(4)

map2.put(a, b, "ab")
map2.put(c, d, "cd")

assertThat(map2.get(a, b), `is`("ab"))
assertThat(map2.get(b, a), `is`("ab"))
assertThat(map2.get(c, d), `is`("cd"))
assertThat(map2.get(d, c), `is`("cd"))
assertThat(map2.values, containsInAnyOrder("ab", "cd"))

map2.remove(a, b)

assertThat(map2.get(a, b), nullValue())
assertThat(map2.get(c, d), `is`("cd"))
}

private class CustomObject
}
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ class PhysicsWorldTest {
gWorld.onUpdate(0.016)
pWorld.onUpdate(0.016)

assertThat(numCollisions, `is`(843))
assertThat("unrotated collisions with $strategy", numCollisions, `is`(843))

// check with rotations now
numCollisions = 0
Expand All @@ -775,7 +775,7 @@ class PhysicsWorldTest {
gWorld.onUpdate(0.016)
pWorld.onUpdate(0.016)

assertThat(numCollisions, `is`(970))
assertThat("rotated collisions with $strategy", numCollisions, `is`(970))
}
}
}
Loading