hash map (-> 0.18)

This commit is contained in:
alex_s168
2024-07-30 17:45:55 +00:00
parent 6eaf69724b
commit 8439df8ae5
9 changed files with 216 additions and 1 deletions

View File

@@ -5,7 +5,7 @@ plugins {
}
group = "me.alex_s168"
version = "0.17"
version = "0.18"
repositories {
mavenCentral()

View File

@@ -0,0 +1,45 @@
package blitz.collections
class BlitzHashMap<K, V>(
private val bucketCount: Int = 16,
private val bucketSrc: DynBuckets<K, V>,
private val hash: (K) -> Int,
): BlitzMap<K, V, BlitzHashMap.Index<K, V>> {
private val buckets = Array(bucketCount) { bucketSrc.new() }
override fun index(key: K): Index<K,V> =
IndexImpl(buckets[hash(key) % bucketCount], key)
private inline fun index(idx: Index<K,V>) =
idx as IndexImpl
override operator fun get(idx: Index<K,V>): V? =
index(idx).let { idx ->
bucketSrc.get(idx.bucket, idx.key)
}
override operator fun set(idx: Index<K,V>, value: V?) {
index(idx).let { idx ->
if (value == null) {
bucketSrc.remove(idx.bucket, idx.key)
} else {
bucketSrc.set(idx.bucket, idx.key, value)
}
}
}
sealed interface Index<K,V>
private class IndexImpl<K,V>(
val bucket: Any?,
val key: K,
): Index<K,V>
override val contents: Contents<Pair<K, V>>
get() = buckets
.map { bucketSrc.contents(it) }
.reduce { acc, pairs -> acc + pairs }
val bucketStats
get() = Contents(buckets.map { bucketSrc.contents(it).count() })
}

View File

@@ -0,0 +1,11 @@
package blitz.collections
interface BlitzMap<K,V,I> {
fun index(key: K): I
operator fun get(index: I): V?
operator fun set(index: I, value: V?)
val contents: Contents<Pair<K,V>>
}
fun <K,V,I> BlitzMap<K,V,I>.remove(index: I) =
set(index, null)

View File

@@ -0,0 +1,75 @@
package blitz.collections
interface Buckets<K, V, B> {
fun new(): B
fun get(bucket: B, key: K): V?
fun set(bucket: B, key: K, value: V)
fun remove(bucket: B, key: K)
fun contents(bucket: B): Contents<Pair<K,V>>
}
class DynBucketsT<K, V, B>(
private val parent: Buckets<K, V, B>
): Buckets<K, V, Any?> {
override fun new(): Any? =
parent.new()
override fun contents(bucket: Any?): Contents<Pair<K, V>> =
parent.contents(bucket as B)
override fun remove(bucket: Any?, key: K) =
parent.remove(bucket as B, key)
override fun set(bucket: Any?, key: K, value: V) =
parent.set(bucket as B, key, value)
override fun get(bucket: Any?, key: K): V? =
parent.get(bucket as B, key)
}
typealias DynBuckets<K,V> = DynBucketsT<K,V,*>
class ListBuckets<K, V>(
private val eq: (K, K) -> Boolean = { a, b -> a == b },
private val construct: () -> MutableList<ListBuckets.Entry<K, V>>
): Buckets<K, V, MutableList<ListBuckets.Entry<K, V>>> {
class Entry<K, V>(val key: K, var value: V): Comparable<ListBuckets.Entry<K, V>> {
override fun compareTo(other: ListBuckets.Entry<K, V>): Int =
(key as? Comparable<K>)?.compareTo(other.key) ?: 0
override fun equals(other: Any?): Boolean =
key == other
}
private fun entry(bucket: MutableList<Entry<K, V>>, key: K) =
bucket.firstOrNull { eq(it.key, key) }
override fun get(bucket: MutableList<Entry<K, V>>, key: K): V? =
entry(bucket, key)?.value
override fun set(bucket: MutableList<Entry<K, V>>, key: K, value: V) {
val entry = entry(bucket, key)
if (entry != null) {
entry.value = value
} else {
bucket.add(Entry(key, value))
}
}
override fun remove(bucket: MutableList<Entry<K, V>>, key: K) {
entry(bucket, key)?.let(bucket::remove)
}
override fun new(): MutableList<Entry<K, V>> =
construct()
override fun contents(bucket: MutableList<Entry<K, V>>): Contents<Pair<K, V>> =
bucket.map { it.key to it.value }.contents
}
fun <K: Comparable<K>, V> SortedListBuckets(
underlying: () -> MutableList<ListBuckets.Entry<K, V>>,
eq: (K, K) -> Boolean = { a, b -> a == b },
) = ListBuckets(eq) {
SortedList(underlying()) { it }
}

View File

@@ -3,6 +3,13 @@ package blitz.collections
class Contents<T> internal constructor(
private val iterable: Iterable<T>
): Iterable<T> {
operator fun plus(other: Contents<T>): Contents<T> {
val li = mutableListOf<T>()
li.addAll(this)
li.addAll(other)
return li.contents
}
override fun iterator(): Iterator<T> =
iterable.iterator()

View File

@@ -0,0 +1,28 @@
package blitz.collections
import blitz.math.cantor
class I2HashMapKey(
val a: Int,
val b: Int
): Comparable<I2HashMapKey> {
val cantorHash = cantor(a, b)
override fun toString() =
"($a, $b)"
override fun hashCode(): Int =
cantorHash
override fun equals(other: Any?) =
other is I2HashMapKey && other.a == a && other.b == b
override fun compareTo(other: I2HashMapKey): Int =
cantorHash.compareTo(other.cantorHash)
}
fun <V> I2HashMap(
underlying: () -> MutableList<ListBuckets.Entry<I2HashMapKey, V>>,
bucketCount: Int = 16,
): BlitzHashMap<I2HashMapKey, V> =
BlitzHashMap(
bucketCount,
DynBuckets(SortedListBuckets(underlying)),
I2HashMapKey::cantorHash
)

View File

@@ -0,0 +1,29 @@
package blitz.collections
import blitz.math.cantor
class I3HashMapKey(
val a: Int,
val b: Int,
val c: Int,
): Comparable<I3HashMapKey> {
val cantorHash = cantor(a, cantor(b, c))
override fun toString() =
"($a, $b, $c)"
override fun hashCode(): Int =
cantorHash
override fun equals(other: Any?) =
other is I3HashMapKey && other.a == a && other.b == b && other.c == c
override fun compareTo(other: I3HashMapKey): Int =
cantorHash.compareTo(other.cantorHash)
}
fun <V> I3HashMap(
underlying: () -> MutableList<ListBuckets.Entry<I3HashMapKey, V>>,
bucketCount: Int = 16,
): BlitzHashMap<I3HashMapKey, V> =
BlitzHashMap(
bucketCount,
DynBuckets(SortedListBuckets(underlying)),
I3HashMapKey::cantorHash
)

View File

@@ -0,0 +1,4 @@
package blitz.math
fun cantor(a: Int, b: Int) =
(a + b + 1) * (a + b) / 2 + b

16
src/test/kotlin/map.kt Normal file
View File

@@ -0,0 +1,16 @@
import blitz.collections.I2HashMap
import blitz.collections.I2HashMapKey
import kotlin.test.Test
class Maps {
@Test
fun i2hashmap() {
val a = I2HashMap<String>(::mutableListOf)
a[a.index(I2HashMapKey(1, 2390))] = "hi"
a[a.index(I2HashMapKey(320, 23))] = "bye"
a[a.index(I2HashMapKey(320, 25))] = "bye2"
a[a.index(I2HashMapKey(32, 344))] = "bye3"
println(a.contents)
println(a.bucketStats)
}
}