From 0f796e6b515a71ffa05c2a76e7d31cb58ea5c19e Mon Sep 17 00:00:00 2001 From: lbressler13 Date: Sun, 17 Mar 2024 13:41:39 -0400 Subject: [PATCH] Shift ConstMultiSet code to impl classes (#63) --- .../set/multiset/const/ConstMultiSet.kt | 202 +----------------- .../set/multiset/const/ConstMultiSetImpl.kt | 38 +++- .../multiset/const/ConstMultiSetManager.kt | 95 ++++++++ .../multiset/const/ConstMutableMultiSet.kt | 150 +------------ .../const/ConstMutableMultiSetImpl.kt | 136 +++++++++++- .../set/multiset/impl/AbstractMultiSetImpl.kt | 3 +- 6 files changed, 272 insertions(+), 352 deletions(-) create mode 100644 kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetManager.kt diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSet.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSet.kt index a04c2a06..6f73d241 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSet.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSet.kt @@ -1,118 +1,13 @@ package xyz.lbres.kotlinutils.set.multiset.const -import xyz.lbres.kotlinutils.general.simpleIf -import xyz.lbres.kotlinutils.general.tryOrDefault import xyz.lbres.kotlinutils.internal.constants.Suppressions import xyz.lbres.kotlinutils.set.multiset.MultiSet -import xyz.lbres.kotlinutils.set.multiset.impl.MultiSetImpl -import xyz.lbres.kotlinutils.set.multiset.utils.countsToList -import xyz.lbres.kotlinutils.set.multiset.utils.countsToString -import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap -import kotlin.math.min /** * [MultiSet] implementation where values of elements are assumed to be constant. * Behavior is not defined if values of elements are changed (i.e. elements are added to a mutable list). */ sealed class ConstMultiSet constructor(private val initialElements: Collection, private var initialCounts: Map? = null) : MultiSet { - /** - * Number of elements in set. - */ - // TODO private - protected var _size: Int = initialElements.size - override val size: Int - get() = _size - - /** - * Map where each key is an element in the set, and each value is the number of occurrences of the element in the set. - * Counts are guaranteed to be greater than 0. - */ - protected open val counts: Map = initializeCounts() - - /** - * All distinct values contained in the set. - */ - override val distinctValues: Set = (initialCounts ?: initializeCounts()).keys - - /** - * String representation of the set - */ - protected open val string: String = countsToString(initialCounts ?: initializeCounts()) - - /** - * All elements in the set - */ - protected open val elements: Collection = initialElements - - /** - * Get the number of occurrences of a given element. - * - * @param element [E] - * @return [Int]: the number of occurrences of [element]. 0 if the element does not exist. - */ - override fun getCountOf(element: E): Int = counts.getOrDefault(element, 0) - - /** - * Determine if an element is contained in the current set. - * - * @param element [E] - * @return [Boolean]: `true` if [element] is in the set, `false` otherwise - */ - override fun contains(element: E): Boolean = counts.contains(element) - - /** - * Determine if all elements in a collection are contained in the current set. - * If the collection contains multiple occurrences of the same value, the function will check if this set contains at least as many occurrences as the collection. - * - * @param elements [Collection] - * @return [Boolean]: `true` if the current set contains at least as many occurrences of each value as the collection, `false` otherwise - */ - override fun containsAll(elements: Collection): Boolean { - val otherCounts = createCountsMap(elements) - - return otherCounts.all { (element, otherCount) -> - otherCount <= getCountOf(element) - } - } - - /** - * Create a new MultiSet with all values from both sets. - * If there are multiple occurrences of a value, the number of occurrences in the other set will be added to the number in this set. - * The returned set is **not** a ConstMultiSet. - * - * @param other [MultiSet]: values to add to this set - * @return [MultiSet]: MultiSet containing all values from both sets - */ - override operator fun plus(other: MultiSet): MultiSet { - val newCounts = combineCounts(other, Int::plus, true) - return MultiSetImpl(countsToList(newCounts)) - } - - /** - * Create a new MultiSet with values that are in this set but not the other set. - * If there are multiple occurrences of a value, the number of occurrences in the other set will be subtracted from the number in this set. - * The returned set is **not** a ConstMultiSet. - * - * @param other [MultiSet]: values to subtract from this set - * @return [MultiSet]: MultiSet containing the items in this set but not the other - */ - override operator fun minus(other: MultiSet): MultiSet { - val newCounts = combineCounts(other, Int::minus, false) - return MultiSetImpl(countsToList(newCounts)) - } - - /** - * Create a new MultiSet with values that are shared between the sets. - * If there are multiple occurrences of a value, the smaller number of occurrences will be used. - * The returned set is **not** a ConstMultiSet. - * - * @param other [MultiSet]: MultiSet to intersect with current - * @return [MultiSet]: MultiSet containing only values that are in both sets - */ - override infix fun intersect(other: MultiSet): MultiSet { - val newCounts = combineCounts(other, { val1, val2 -> min(val1, val2) }, false) - return MultiSetImpl(countsToList(newCounts)) - } @Suppress(Suppressions.FUNCTION_NAME) infix fun `+c`(other: ConstMultiSet): ConstMultiSet = plusC(other) @@ -125,10 +20,7 @@ sealed class ConstMultiSet constructor(private val initialElements: Collectio * @param other [ConstMultiSet]: values to add to this set * @return [ConstMultiSet]: ConstMultiSet containing all values from both sets */ - fun plusC(other: ConstMultiSet): ConstMultiSet { - val newCounts = combineCounts(other, Int::plus, true) - return ConstMultiSetImpl(countsToList(newCounts), newCounts) - } + abstract fun plusC(other: ConstMultiSet): ConstMultiSet /** * Alternate version of [minus], which returns a ConstMultiSet @@ -136,10 +28,7 @@ sealed class ConstMultiSet constructor(private val initialElements: Collectio * @param other [ConstMultiSet]: values to subtract from this set * @return [ConstMultiSet]: ConstMultiSet containing the items in this set but not the other */ - fun minusC(other: ConstMultiSet): ConstMultiSet { - val newCounts = combineCounts(other, Int::minus, false) - return ConstMultiSetImpl(countsToList(newCounts), newCounts) - } + abstract fun minusC(other: ConstMultiSet): ConstMultiSet /** * Alternate version of [intersect], which returns a ConstMultiSet @@ -147,90 +36,5 @@ sealed class ConstMultiSet constructor(private val initialElements: Collectio * @param other [ConstMultiSet]: ConstMultiSet to intersect with current * @return [ConstMultiSet]: ConstMultiSet containing only values that are in both sets */ - infix fun intersectC(other: ConstMultiSet): ConstMultiSet { - val newCounts = combineCounts(other, { val1, val2 -> min(val1, val2) }, false) - return ConstMultiSetImpl(countsToList(newCounts), newCounts) - } - - /** - * Combine counts with another MultiSet, using the given operation - * - * @param other [MultiSet]: MultiSet to combine with - * @param operation (Int, Int) -> Int: combination function - * @param useAllValues [Boolean]: if all values from both sets should be used to generate the new set. If `false`, only the values from this set will be used. - * @return [Map]: new counts map where each element has the number of occurrences specified by the operation - */ - private fun combineCounts(other: MultiSet, operation: (count: Int, otherCount: Int) -> Int, useAllValues: Boolean): Map { - val values = simpleIf(useAllValues, { distinctValues + other.distinctValues }, { distinctValues }) - return values.associateWith { - operation(getCountOf(it), other.getCountOf(it)) - }.filter { it.value > 0 } - } - - /** - * If the current set contains 0 elements. - * - * @return [Boolean]: `true` if the set contains 0 elements, `false` otherwise - */ - override fun isEmpty(): Boolean = size == 0 - - /** - * If the current set contains the same elements as another MultiSet, with the same number of occurrences per element. - * - * @param other [Any]? - * @return [Boolean]: `true` if [other] is a non-null MultiSet which contains the same values as the current set, `false` otherwise - */ - override fun equals(other: Any?): Boolean { - if (other == null || other !is MultiSet<*>) { - return false - } - - @Suppress(Suppressions.UNCHECKED_CAST) - return tryOrDefault(false, listOf(ClassCastException::class)) { - if (other is ConstMultiSet<*>) { - other as ConstMultiSet - counts == other.counts - } else { - other as MultiSet - size == other.size && distinctValues.all { getCountOf(it) == other.getCountOf(it) } - } - } - } - - /** - * Get a string representation of the set. - * - * @return [String] - */ - override fun toString(): String = string - - /** - * Get an iterator for the elements in this set. - * - * @return [Iterator] - */ - override fun iterator(): Iterator = elements.iterator() - - /** - * Get hash code for set. - * - * @return [Int] - */ - override fun hashCode(): Int { - val hashCode = counts.hashCode() - return 31 * hashCode + MultiSet::class.java.name.hashCode() - } - - /** - * Generate a counts map from the initial elements - * - * @return [Map]: generated map - */ - protected fun initializeCounts(): Map { - if (initialCounts == null) { - initialCounts = createCountsMap(initialElements) - } - - return initialCounts!! - } + abstract infix fun intersectC(other: ConstMultiSet): ConstMultiSet } diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetImpl.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetImpl.kt index 963c4727..dbd12c66 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetImpl.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetImpl.kt @@ -1,4 +1,40 @@ package xyz.lbres.kotlinutils.set.multiset.const +import xyz.lbres.kotlinutils.generic.ext.ifNull +import xyz.lbres.kotlinutils.set.multiset.MultiSet +import xyz.lbres.kotlinutils.set.multiset.utils.countsToString +import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap + // final implementation of ConstMultiSet -internal class ConstMultiSetImpl(initialElements: Collection, initialCounts: Map? = null) : ConstMultiSet(initialElements, initialCounts) +internal class ConstMultiSetImpl(private val elements: Collection, initialCounts: Map? = null) : ConstMultiSet(elements, initialCounts) { + private val manager: ConstMultiSetManager + override val size: Int = elements.size + override val distinctValues: Set + private val string: String + val counts: Map + + init { + counts = initialCounts.ifNull { createCountsMap(elements) } + distinctValues = counts.keys + manager = ConstMultiSetManager(counts) + string = countsToString(counts) + } + + override fun getCountOf(element: E): Int = manager.getCountOf(element) + override fun contains(element: E): Boolean = manager.contains(element) + override fun containsAll(elements: Collection): Boolean = manager.containsAll(elements) + + override fun plus(other: MultiSet): MultiSet = manager.plus(other) + override fun minus(other: MultiSet): MultiSet = manager.minus(other) + override fun intersect(other: MultiSet): MultiSet = manager.intersect(other) + + override fun plusC(other: ConstMultiSet): ConstMultiSet = manager.plusC(other) + override fun minusC(other: ConstMultiSet): ConstMultiSet = manager.minusC(other) + override fun intersectC(other: ConstMultiSet): ConstMultiSet = manager.intersectC(other) + + override fun isEmpty(): Boolean = manager.isEmpty() + override fun iterator(): Iterator = elements.iterator() + override fun hashCode(): Int = manager.getHashCode() + override fun equals(other: Any?): Boolean = manager.equalsSet(other) + override fun toString(): String = string +} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetManager.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetManager.kt new file mode 100644 index 00000000..47b07e5c --- /dev/null +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMultiSetManager.kt @@ -0,0 +1,95 @@ +package xyz.lbres.kotlinutils.set.multiset.const + +import xyz.lbres.kotlinutils.general.simpleIf +import xyz.lbres.kotlinutils.set.multiset.MultiSet +import xyz.lbres.kotlinutils.set.multiset.impl.MultiSetImpl +import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap +import kotlin.math.min + +/** + * Manager class for common ConstMultiSet code + * + * @param counts [Map]: counts map for set + */ +internal class ConstMultiSetManager(private val counts: Map) { + fun getCountOf(element: E): Int = counts.getOrDefault(element, 0) + fun isEmpty(): Boolean = counts.isEmpty() + + fun contains(element: E): Boolean = counts.contains(element) + fun containsAll(elements: Collection): Boolean { + val otherCounts = createCountsMap(elements) + + return otherCounts.all { (element, otherCount) -> + otherCount <= getCountOf(element) + } + } + + fun plus(other: MultiSet): MultiSet { + return combineCounts(other, Int::plus, true, const = false) + } + fun minus(other: MultiSet): MultiSet { + return combineCounts(other, Int::minus, false, const = false) + } + fun intersect(other: MultiSet): MultiSet { + return combineCounts(other, ::min, false, const = false) + } + fun plusC(other: ConstMultiSet): ConstMultiSet { + return combineCounts(other, Int::plus, true, const = true) as ConstMultiSet + } + fun minusC(other: ConstMultiSet): ConstMultiSet { + return combineCounts(other, Int::minus, false, const = true) as ConstMultiSet + } + fun intersectC(other: ConstMultiSet): ConstMultiSet { + return combineCounts(other, ::min, false, const = true) as ConstMultiSet + } + + /** + * Combine counts with another MultiSet, using the given operation + * + * @param other [MultiSet]: MultiSet to combine with + * @param operation (Int, Int) -> Int: combination function + * @param useAllValues [Boolean]: if all values from both sets should be used to generate the new set. If `false`, only the values from this set will be used. + * @param const [Boolean]: if the returned MultiSet should be a ConstMultiSet + * @return [MultiSet]: new set where each element has the number of occurrences specified by the operation. If [const] is `true`, the set will be a const multi set + */ + private fun combineCounts(other: MultiSet, operation: (count: Int, otherCount: Int) -> Int, useAllValues: Boolean, const: Boolean): MultiSet { + val values: Set = simpleIf(useAllValues, { counts.keys + other.distinctValues }, { counts.keys }) + val newCounts: MutableMap = mutableMapOf() + val newElements: MutableList = mutableListOf() + + values.forEach { value -> + val count = operation(getCountOf(value), other.getCountOf(value)) + if (count > 0) { + newCounts[value] = count + repeat(count) { newElements.add(value) } + } + } + + return simpleIf(const, { ConstMultiSetImpl(newElements, newCounts) }, { MultiSetImpl(newElements) }) + } + + /** + * Check equality of the managed set to another MultiSet + * + * @param other [Any]? + * @return [Boolean] `true` if [other] is a MultiSet containing the same elements as the counts map, `false` otherwise + */ + fun equalsSet(other: Any?): Boolean { + return when (other) { + is ConstMultiSetImpl<*> -> counts == other.counts + is ConstMutableMultiSetImpl<*> -> counts == other.counts + is MultiSet<*> -> counts == createCountsMap(other) + else -> false + } + } + + /** + * Get hash code for set. + * + * @return [Int] + */ + fun getHashCode(): Int { + val hashCode = counts.hashCode() + return 31 * hashCode + MultiSet::class.java.name.hashCode() + } +} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSet.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSet.kt index f2e90d63..e58813d7 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSet.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSet.kt @@ -1,164 +1,16 @@ package xyz.lbres.kotlinutils.set.multiset.const import xyz.lbres.kotlinutils.set.multiset.MutableMultiSet -import xyz.lbres.kotlinutils.set.multiset.utils.countsToList -import xyz.lbres.kotlinutils.set.multiset.utils.countsToString -import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap -import kotlin.math.min /** * [MutableMultiSet] implementation where values of elements are assumed to be constant. * Behavior is not defined if values of elements are changed (i.e. elements are added to a mutable list). */ sealed class ConstMutableMultiSet constructor(initialElements: Collection) : MutableMultiSet, ConstMultiSet(initialElements) { - /** - * If all properties are up-to-date with the most recent changes to the counts map - */ - private var allPropertiesUpdated: Boolean = false - - // elements list is up-to-date only when allPropertiesUpdated == true - private var _elements: List = initialElements.toList() - override val elements: Collection - get() { - updateMutableValues() // update elements before returning value - return _elements - } - - // string is up-to-date only when allPropertiesUpdated == true - private var _string: String = "" - override val string: String - get() { - updateMutableValues() // update string before returning value - return _string - } - - override val counts: MutableMap = initializeCounts().toMutableMap() - - override val distinctValues: Set - get() = counts.keys - - /** - * Add one occurrence of the specified element to the set. - * - * @param element [E] - * @return [Boolean]: `true` if the element has been added successfully, `false` otherwise - */ - override fun add(element: E): Boolean { - allPropertiesUpdated = false - counts[element] = getCountOf(element) + 1 - _size++ - return true - } - - /** - * Add all specified elements to the set. - * If [elements] contains multiple occurrences of the same value, the value will be added multiple times. - * - * @param elements [Collection] - * @return [Boolean]: `true` if any elements have been added successfully, `false` otherwise - */ - override fun addAll(elements: Collection): Boolean { - allPropertiesUpdated = false - elements.forEach(this::add) - return true - } - - /** - * Remove one occurrence of the specified element from the set, if the element exists. - * - * @param element [E] - * @return [Boolean]: `true` if the element has been removed successfully, `false` otherwise - */ - override fun remove(element: E): Boolean { - when (getCountOf(element)) { - 0 -> return false - 1 -> counts.remove(element) - else -> counts[element] = getCountOf(element) - 1 - } - - allPropertiesUpdated = false - _size-- - return true - } - - /** - * Remove all specified elements from the set. - * If [elements] contains a single occurrence of a value, only one occurrence of the value will be removed from the set. - * If there are multiple occurrences, the value will be removed multiple times. - * - * @param elements [Collection] - * @return [Boolean]: `true` if any elements have been removed successfully, `false` otherwise - */ - override fun removeAll(elements: Collection): Boolean { - if (elements.isEmpty()) { - return true - } - - var anySucceeded = false - elements.forEach { anySucceeded = remove(it) || anySucceeded } - allPropertiesUpdated = allPropertiesUpdated && !anySucceeded - - return anySucceeded - } - - /** - * Retain only elements that are present in [elements]. - * If [elements] contains multiple occurrences of the same value, the value will retain up to that number of occurrences. - * - * @param elements [Collection] - * @return [Boolean]: `true` if elements have been retained successfully, `false` otherwise - */ - override fun retainAll(elements: Collection): Boolean { - val initialSize = size - val elementsCounts = createCountsMap(elements) - - val newCounts = distinctValues.associateWith { - min(getCountOf(it), elementsCounts.getOrDefault(it, 0)) - } - - counts.clear() - _size = 0 - - newCounts.forEach { (element, count) -> - if (count > 0) { - counts[element] = count - _size += count - } - } - - allPropertiesUpdated = allPropertiesUpdated && size == initialSize - - return true - } - - /** - * Remove all elements from the set. - */ - override fun clear() { - counts.clear() - _size = 0 - allPropertiesUpdated = false - } - /** * Get an iterator for the elements in this set. * * @return [MutableIterator] */ - override fun iterator(): MutableIterator { - updateMutableValues() - return _elements.toMutableList().iterator() - } - - /** - * Update the elements list and string based on the current values in the counts map - */ - private fun updateMutableValues() { - if (!allPropertiesUpdated) { - _elements = countsToList(counts) - _string = countsToString(counts) - - allPropertiesUpdated = true - } - } + abstract override fun iterator(): MutableIterator } diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetImpl.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetImpl.kt index 831ef465..bd01623c 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetImpl.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/const/ConstMutableMultiSetImpl.kt @@ -1,4 +1,138 @@ package xyz.lbres.kotlinutils.set.multiset.const +import xyz.lbres.kotlinutils.set.multiset.MultiSet +import xyz.lbres.kotlinutils.set.multiset.utils.countsToList +import xyz.lbres.kotlinutils.set.multiset.utils.countsToString +import xyz.lbres.kotlinutils.set.multiset.utils.createCountsMap +import kotlin.math.min + // final implementation of ConstMutableMultiSet -internal class ConstMutableMultiSetImpl(initialElements: Collection) : ConstMutableMultiSet(initialElements) +internal class ConstMutableMultiSetImpl(initialElements: Collection) : ConstMutableMultiSet(initialElements) { + /** + * If all properties are up-to-date with the most recent changes to the counts map + */ + private var allPropertiesUpdated: Boolean = false + + private val manager: ConstMultiSetManager + private val _counts: MutableMap + val counts: Map + + override val distinctValues: Set + get() = _counts.keys + + // elements list is up-to-date only when allPropertiesUpdated == true + private var elements: List = initialElements.toList() + + private var _size: Int = elements.size + override val size: Int + get() = _size + + // string is up-to-date only when allPropertiesUpdated == true + private var string: String = "" + + init { + _counts = createCountsMap(initialElements).toMutableMap() + counts = _counts + manager = ConstMultiSetManager(_counts) + } + + override fun add(element: E): Boolean { + allPropertiesUpdated = false + _counts[element] = getCountOf(element) + 1 + _size++ + return true + } + + override fun addAll(elements: Collection): Boolean { + allPropertiesUpdated = false + elements.forEach(this::add) + return true + } + + override fun remove(element: E): Boolean { + when (getCountOf(element)) { + 0 -> return false + 1 -> _counts.remove(element) + else -> _counts[element] = getCountOf(element) - 1 + } + + allPropertiesUpdated = false + _size-- + return true + } + + override fun removeAll(elements: Collection): Boolean { + if (elements.isEmpty()) { + return true + } + + val anySucceeded = elements.fold(false) { succeeded, element -> remove(element) || succeeded } + allPropertiesUpdated = allPropertiesUpdated && !anySucceeded + + return anySucceeded + } + + override fun retainAll(elements: Collection): Boolean { + val initialSize = size + val elementsCounts = createCountsMap(elements) + + _size = 0 + distinctValues.forEach { + val newCount = min(getCountOf(it), elementsCounts.getOrDefault(it, 0)) + if (newCount > 0) { + _counts[it] = newCount + _size += newCount + } else { + _counts.remove(it) + } + } + + allPropertiesUpdated = allPropertiesUpdated && size == initialSize + + return true + } + + override fun clear() { + _counts.clear() + _size = 0 + allPropertiesUpdated = false + } + + override fun getCountOf(element: E): Int = manager.getCountOf(element) + override fun contains(element: E): Boolean = manager.contains(element) + override fun containsAll(elements: Collection): Boolean = manager.containsAll(elements) + + override fun plus(other: MultiSet): MultiSet = manager.plus(other) + override fun minus(other: MultiSet): MultiSet = manager.minus(other) + override fun intersect(other: MultiSet): MultiSet = manager.intersect(other) + + override fun plusC(other: ConstMultiSet): ConstMultiSet = manager.plusC(other) + override fun minusC(other: ConstMultiSet): ConstMultiSet = manager.minusC(other) + override fun intersectC(other: ConstMultiSet): ConstMultiSet = manager.intersectC(other) + + override fun isEmpty(): Boolean = manager.isEmpty() + override fun equals(other: Any?): Boolean = manager.equalsSet(other) + override fun hashCode(): Int = manager.getHashCode() + + override fun iterator(): MutableIterator { + updateMutableValues() + return elements.toMutableList().iterator() + } + + override fun toString(): String { + updateMutableValues() // update string before returning value + return string + } + + /** + * Update the elements list and string based on the current values in the counts map + */ + private fun updateMutableValues() { + if (!allPropertiesUpdated) { + elements = countsToList(_counts) + string = countsToString(_counts) + + allPropertiesUpdated = true + } + } +} diff --git a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/AbstractMultiSetImpl.kt b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/AbstractMultiSetImpl.kt index d8adbdbd..cd59f416 100644 --- a/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/AbstractMultiSetImpl.kt +++ b/kotlin-utils/src/main/kotlin/xyz/lbres/kotlinutils/set/multiset/impl/AbstractMultiSetImpl.kt @@ -27,8 +27,7 @@ internal abstract class AbstractMultiSetImpl(private val initialElements: Col /** * Elements in the set. */ - protected open val elements: Collection - get() = initialElements + protected open val elements: Collection = initialElements /** * Get the number of occurrences of a given element.