From 40459a7e99615dab09059a40df28bda39f81ce58 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 3 Feb 2024 18:27:52 +0700 Subject: [PATCH] WeakHashSet --- .../kommons/collect/ReferenceHashStrategy.kt | 25 ++++++ .../kommons/collect/WeakHashSet.kt | 78 +++++++++++++++++++ .../kommons/util/HashedWeakReference.kt | 28 +++++++ 3 files changed, 131 insertions(+) create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/collect/ReferenceHashStrategy.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/collect/WeakHashSet.kt create mode 100644 src/main/kotlin/ru/dbotthepony/kommons/util/HashedWeakReference.kt diff --git a/src/main/kotlin/ru/dbotthepony/kommons/collect/ReferenceHashStrategy.kt b/src/main/kotlin/ru/dbotthepony/kommons/collect/ReferenceHashStrategy.kt new file mode 100644 index 0000000..d656a12 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/collect/ReferenceHashStrategy.kt @@ -0,0 +1,25 @@ +package ru.dbotthepony.kommons.collect + +import it.unimi.dsi.fastutil.Hash +import java.lang.ref.Reference + +object ReferenceHashStrategy : Hash.Strategy { + override fun equals(a: Any?, b: Any?): Boolean { + if (a === b) return true + if (a == null || b == null) return false + + if (a is Reference<*>) { + val ref = a.get() + if (b is Reference<*>) return ref != null && ref == b.get() + return ref == b + } else if (b is Reference<*>) { + return b.get() == a + } else { + return a == b + } + } + + override fun hashCode(o: Any?): Int { + return o.hashCode() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/collect/WeakHashSet.kt b/src/main/kotlin/ru/dbotthepony/kommons/collect/WeakHashSet.kt new file mode 100644 index 0000000..1fc821a --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/collect/WeakHashSet.kt @@ -0,0 +1,78 @@ +package ru.dbotthepony.kommons.collect + +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenCustomHashSet +import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet +import it.unimi.dsi.fastutil.objects.ObjectSet +import ru.dbotthepony.kommons.util.HashedWeakReference +import java.lang.ref.ReferenceQueue + +class WeakHashSet(initialCapacity: Int = 16, loadFactor: Float = 0.75f, linked: Boolean = false) : MutableSet { + private val queue = ReferenceQueue() + private val backing: ObjectSet = if (linked) ObjectLinkedOpenCustomHashSet(initialCapacity, loadFactor, ReferenceHashStrategy) else ObjectOpenCustomHashSet(initialCapacity, loadFactor, ReferenceHashStrategy) + + private fun purge() { + var next = queue.poll() + + while (next != null) { + backing.remove(next) + next = queue.poll() + } + } + + override fun add(element: E): Boolean { + purge() + if (element in backing) return false + return backing.add(HashedWeakReference(element, queue)) + } + + override fun addAll(elements: Collection): Boolean { + var any = false + elements.forEach { any = add(it) || any } + return any + } + + override fun clear() { + backing.clear() + while (queue.poll() != null) {} + } + + override fun iterator(): MutableIterator { + purge() + return backing.iterator().map { (it as HashedWeakReference).get() }.filterNotNull() + } + + override fun remove(element: E): Boolean { + purge() + return backing.remove(element) + } + + override fun removeAll(elements: Collection): Boolean { + purge() + return backing.removeAll(elements) + } + + override fun retainAll(elements: Collection): Boolean { + purge() + return backing.retainAll(elements) + } + + override val size: Int get() { + purge() + return backing.size + } + + override fun contains(element: E): Boolean { + purge() + return backing.contains(element) + } + + override fun containsAll(elements: Collection): Boolean { + purge() + return backing.containsAll(elements) + } + + override fun isEmpty(): Boolean { + purge() + return backing.isEmpty() + } +} diff --git a/src/main/kotlin/ru/dbotthepony/kommons/util/HashedWeakReference.kt b/src/main/kotlin/ru/dbotthepony/kommons/util/HashedWeakReference.kt new file mode 100644 index 0000000..906987f --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/kommons/util/HashedWeakReference.kt @@ -0,0 +1,28 @@ +package ru.dbotthepony.kommons.util + +import java.lang.ref.ReferenceQueue +import java.lang.ref.WeakReference + +/** + * [WeakReference], but with [hashCode] overridden with hash of referent object + */ +@Suppress("EqualsOrHashCode") +class HashedWeakReference : WeakReference { + constructor(value: T) : super(value) { + hash = value.hashCode() + } + + constructor(value: T, queue: ReferenceQueue) : super(value, queue) { + hash = value.hashCode() + } + + private val hash: Int + + override fun hashCode(): Int { + return hash + } + + override fun toString(): String { + return "HashedWeakReference[hash=$hash]" + } +}