All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dorkbox.collections.ArrayMap.kt Maven / Gradle / Ivy

/*
 * Copyright 2023 dorkbox, llc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*******************************************************************************
 * Copyright 2011 LibGDX.
 * Mario Zechner @gmail.com>
 * Nathan Sweet @gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dorkbox.collections

import dorkbox.collections.Collections.allocateIterators
import dorkbox.collections.Collections.random
import dorkbox.collections.ObjectMap.Companion.dummy
import java.lang.IllegalStateException
import java.util.*
import kotlin.math.max
import kotlin.math.min

/**
 * An ordered or unordered map of objects. This implementation uses arrays to store the keys and values, which means
 * [gets][.getKey] do a comparison for each key in the map. This is slower than a typical hash map
 * implementation, but may be acceptable for small maps and has the benefits that keys and values can be accessed by index, which
 * makes iteration fast. If ordered is false, this class avoids a memory copy when removing elements (the last
 * element is moved to the removed element's position).
 *
 * @author Nathan Sweet
 */
class ArrayMap : MutableMap{
    companion object {
        const val version = Collections.version
    }

    var keyTable: Array
    var valueTable: Array
    var size_ = 0
    var ordered: Boolean

    @Transient
    private var entries1: Entries? = null

    @Transient
    private var entries2: Entries? = null

    @Transient
    private var values1: Values? = null

    @Transient
    private var values2: Values? = null

    @Transient
    private var keys1: Keys? = null

    @Transient
    private var keys2: Keys? = null

    /**
     * Creates an ordered map with the default capacity.
     */
    constructor() : this(true, 16)

    /**
     * Creates an ordered map with the specified capacity.
     */
    constructor(capacity: Int) : this(true, capacity)

    /**
     * Creates an ordered map
     *
     * @param ordered If false, methods that remove elements may change the order of other elements in the arrays, which avoids a
     * memory copy.
     * @param capacity Any elements added beyond this will cause the backing arrays to be grown.
     */
    @Suppress("UNCHECKED_CAST")
    constructor(ordered: Boolean = true, capacity: Int = 16) {
        this.ordered = ordered
        keyTable = arrayOfNulls(capacity) as Array
        valueTable = arrayOfNulls(capacity) as Array
    }

    /**
     * Creates a new map with [.keys] and [.values] of the specified type.
     *
     * @param ordered If false, methods that remove elements may change the order of other elements in the arrays, which avoids a
     * memory copy.
     * @param capacity Any elements added beyond this will cause the backing arrays to be grown.
     */
    constructor(ordered: Boolean, capacity: Int, keyArrayType: Class, valueArrayType: Class) {
        this.ordered = ordered

        @Suppress("UNCHECKED_CAST")
        keyTable = java.lang.reflect.Array.newInstance(keyArrayType, capacity) as Array

        @Suppress("UNCHECKED_CAST")
        valueTable = java.lang.reflect.Array.newInstance(valueArrayType, capacity) as Array
    }

    /**
     * Creates an ordered map with [.keys] and [.values] of the specified type and a capacity of 16.
     */
    constructor(keyArrayType: Class, valueArrayType: Class) : this(false, 16, keyArrayType, valueArrayType)

    /**
     * Creates a new map containing the elements in the specified map. The new map will have the same type of backing arrays and
     * will be ordered if the specified map is ordered. The capacity is set to the number of elements, so any subsequent elements
     * added will cause the backing arrays to be grown.
     */
    @Suppress("UNCHECKED_CAST")
    constructor(array: ArrayMap) : this(
        array.ordered,
        array.size_,
        array.keyTable.javaClass.componentType as Class,
        array.valueTable.javaClass.componentType as Class
    ) {
        size_ = array.size_
        System.arraycopy(array.keyTable, 0, keyTable, 0, size_)
        System.arraycopy(array.valueTable, 0, valueTable, 0, size_)
    }

    override fun put(key: K, value: V?): V? {
        val oldValue = get(key)
        putIndex(key, value)
        return oldValue
    }

    /**
     * @return the index within the backing arrays where the item is put
     */
    fun putIndex(key: K, value: V?): Int {
        var index = indexOfKey(key)
        if (index == -1) {
            if (size_ == keyTable.size) resize(max(8.0, (size_ * 1.75f).toInt().toDouble()).toInt())
            index = size_++
        }

        keyTable[index] = key
        valueTable[index] = value
        return index
    }

    /**
     * @return the actual index within the backing arrays where the item is put
     */
    fun put(key: K, value: V, index: Int): Int {
        val existingIndex = indexOfKey(key)
        if (existingIndex != -1) {
            removeIndex(existingIndex)
        }
        else if (size_ == keyTable.size) {
            resize(max(8.0, (size_ * 1.75f).toInt().toDouble()).toInt())
        }

        System.arraycopy(keyTable, index, keyTable, index + 1, size_ - index)
        System.arraycopy(valueTable, index, valueTable, index + 1, size_ - index)

        keyTable[index] = key
        valueTable[index] = value
        size_++
        return index
    }

    override fun remove(key: K): V? {
        return removeKey(key)
    }

    override fun putAll(from: Map) {
        from.forEach { (k,v) ->
            put(k, v)
        }
    }

    fun putAll(map: ArrayMap, offset: Int = 0, length: Int = map.size_) {
        if (offset + length > map.size_) { throw StateException("offset + length must be <= size: $offset + $length <= ${map.size_}") }

        val sizeNeeded = size_ + length - offset
        if (sizeNeeded >= keyTable.size) {
            resize(max(8.0, (sizeNeeded * 1.75f).toInt().toDouble()).toInt())
        }

        System.arraycopy(map.keyTable, offset, keyTable, size_, length)
        System.arraycopy(map.valueTable, offset, valueTable, size_, length)
        size_ += length
    }

    /**
     * Returns the value (which may be null) for the specified key, or null if the key is not in the map. Note this does a
     * .equals() comparison of each key in reverse order until the specified key is found.
     */
    override operator fun get(key: K): V? {
        val keys = keyTable
        var i = size_ - 1
        if (key == null) {
            while (i >= 0) {
                if (keys[i] === key) return valueTable[i]!!
                i--
            }
        }
        else {
            while (i >= 0) {
                if (key == keys[i]) return valueTable[i]!!
                i--
            }
        }
        return null
    }

    /**
     * Returns the value (which may be null) for the specified key, or the default value if the key is not in the map. Note this
     * does a .equals() comparison of each key in reverse order until the specified key is found.
     */
    operator fun get(key: K?, defaultValue: V): V? {
        val keys = keyTable
        var i = size_ - 1
        if (key == null) {
            while (i >= 0) {
                if (keys[i] === key) return valueTable[i]
                i--
            }
        }
        else {
            while (i >= 0) {
                if (key == keys[i]) return valueTable[i]
                i--
            }
        }

        return defaultValue
    }

    /**
     * Returns the key for the specified value.
     * Note this does a comparison of each value in reverse order until the specified value is found.
     *
     * @param identity If true, == comparison will be used. If false, .equals() comparison will be used.
     */
    fun getKey(value: V?, identity: Boolean): K? {
        val values = valueTable
        var i = size_ - 1

        if (identity || value == null) {
            while (i >= 0) {
                if (values[i] === value) {
                    return keyTable[i]
                }
                i--
            }
        }
        else {
            while (i >= 0) {
                if (value == values[i]) {
                    return keyTable[i]
                }
                i--
            }
        }
        return null
    }

    fun getKeyAt(index: Int): K? {
        if (index >= size_) throw IndexOutOfBoundsException(index.toString())
        return keyTable[index]
    }

    fun getValueAt(index: Int): V? {
        if (index >= size_) throw IndexOutOfBoundsException(index.toString())
        return valueTable[index]
    }

    fun firstKey(): K? {
        check(size_ != 0) { "Map is empty." }
        return keyTable[0]
    }

    fun firstValue(): V? {
        check(size_ != 0) { "Map is empty." }
        return valueTable[0]
    }

    fun setKey(index: Int, key: K) {
        if (index >= size_) throw IndexOutOfBoundsException(index.toString())
        keyTable[index] = key
    }

    fun setValue(index: Int, value: V) {
        if (index >= size_) throw IndexOutOfBoundsException(index.toString())
        valueTable[index] = value
    }

    fun insert(index: Int, key: K, value: V) {
        if (index > size_) throw IndexOutOfBoundsException(index.toString())
        if (size_ == keyTable.size) resize(max(8.0, (size_ * 1.75f).toInt().toDouble()).toInt())
        if (ordered) {
            System.arraycopy(keyTable, index, keyTable, index + 1, size_ - index)
            System.arraycopy(valueTable, index, valueTable, index + 1, size_ - index)
        }
        else {
            keyTable[size_] = keyTable[index]
            valueTable[size_] = valueTable[index]
        }
        size_++
        keyTable[index] = key
        valueTable[index] = value
    }

    override fun containsKey(key: K): Boolean {
        return containsAnyKey(key)
    }

    fun containsNullKey(): Boolean {
        return containsAnyKey(null)
    }

    private fun containsAnyKey(key: K?): Boolean {
        val keys = keyTable
        var i = size_ - 1
        if (key == null) {
            while (i >= 0) if (key === keys[i--]) return true
        }
        else {
            while (i >= 0) if (key == keys[i--]) return true
        }
        return false
    }

    override fun containsValue(value: V?): Boolean {
        return containsValue(value, false)
    }

    /**
     * @param identity If true, == comparison will be used. If false, .equals() comparison will be used.
     */
    fun containsValue(value: V?, identity: Boolean): Boolean {
        val values = valueTable
        var i = size_ - 1
        if (identity || value == null) {
            while (i >= 0) if (values[i--] === value) return true
        }
        else {
            while (i >= 0) if (value == values[i--]) return true
        }
        return false
    }

    fun indexOfKey(key: K?): Int {
        val keys = keyTable
        if (key == null) {
            var i = 0
            val n = size_
            while (i < n) {
                if (keys[i] === key) return i
                i++
            }
        }
        else {
            var i = 0
            val n = size_
            while (i < n) {
                if (key == keys[i]) return i
                i++
            }
        }
        return -1
    }

    fun indexOfValue(value: V?, identity: Boolean): Int {
        val values = valueTable
        if (identity || value == null) {
            var i = 0
            val n = size_
            while (i < n) {
                if (values[i] === value) return i
                i++
            }
        }
        else {
            var i = 0
            val n = size_
            while (i < n) {
                if (value == values[i]) return i
                i++
            }
        }
        return -1
    }

    fun removeKey(key: K?): V? {
        val keys = keyTable
        if (key == null) {
            var i = 0
            val n = size_
            while (i < n) {
                if (keys[i] === key) {
                    val value = valueTable[i]
                    removeIndex(i)
                    return value
                }
                i++
            }
        }
        else {
            var i = 0
            val n = size_
            while (i < n) {
                if (key == keys[i]) {
                    val value = valueTable[i]
                    removeIndex(i)
                    return value
                }
                i++
            }
        }
        return null
    }

    fun removeValue(value: V?, identity: Boolean): Boolean {
        val values = valueTable
        if (identity || value == null) {
            var i = 0
            val n = size_
            while (i < n) {
                if (values[i] === value) {
                    removeIndex(i)
                    return true
                }
                i++
            }
        }
        else {
            var i = 0
            val n = size_
            while (i < n) {
                if (value == values[i]) {
                    removeIndex(i)
                    return true
                }
                i++
            }
        }
        return false
    }

    /** Removes and returns the key/values pair at the specified index.  */
    fun removeIndex(index: Int) {
        if (index >= size_) throw IndexOutOfBoundsException(index.toString())
        val keys = keyTable
        size_--
        if (ordered) {
            System.arraycopy(keys, index + 1, keys, index, size_ - index)
            System.arraycopy(valueTable, index + 1, valueTable, index, size_ - index)
        }
        else {
            keys[index] = keys[size_]
            valueTable[index] = valueTable[size_]
        }
        keys[size_] = null
        valueTable[size_] = null
    }

    /** Returns true if the map has one or more items.  */
    fun notEmpty(): Boolean {
        return size_ > 0
    }

    /** Returns true if the map is empty.  */
    override fun isEmpty(): Boolean {
        return size_ == 0
    }

    /** Returns the last key.  */
    fun peekKey(): K? {
        return keyTable[size_ - 1]
    }

    /** Returns the last value.  */
    fun peekValue(): V? {
        return valueTable[size_ - 1]
    }

    /** Clears the map and reduces the size of the backing arrays to be the specified capacity if they are larger.  */
    fun clear(maximumCapacity: Int) {
        if (keyTable.size <= maximumCapacity) {
            clear()
            return
        }
        size_ = 0
        resize(maximumCapacity)
    }
    override val size: Int
        get() = size_

    override fun clear() {
        Arrays.fill(keyTable, 0, size_, null)
        Arrays.fill(valueTable, 0, size_, null)
        size_ = 0
    }


    /**
     * Reduces the size of the backing arrays to the size of the actual number of entries. This is useful to release memory when
     * many items have been removed, or if it is known that more entries will not be added.
     */
    fun shrink() {
        if (keyTable.size == size_) return
        resize(size_)
    }

    /**
     * Increases the size of the backing arrays to accommodate the specified number of additional entries. Useful before adding
     * many entries to avoid multiple backing array resizes.
     */
    fun ensureCapacity(additionalCapacity: Int) {
        if (additionalCapacity < 0) { throw StateException("additionalCapacity must be >= 0: $additionalCapacity") }

        val sizeNeeded = size_ + additionalCapacity
        if (sizeNeeded > keyTable.size) resize(
            max(max(8.0, sizeNeeded.toDouble()), (size_ * 1.75f).toInt().toDouble()).toInt()
        )
    }

    protected fun resize(newSize: Int) {
        @Suppress("UNCHECKED_CAST")
        val newKeys = java.lang.reflect.Array.newInstance(keyTable.javaClass.componentType, newSize) as Array
        System.arraycopy(keyTable, 0, newKeys, 0, min(size_.toDouble(), newKeys.size.toDouble()).toInt())
        keyTable = newKeys

        @Suppress("UNCHECKED_CAST")
        val newValues = java.lang.reflect.Array.newInstance(valueTable.javaClass.componentType, newSize) as Array
        System.arraycopy(valueTable, 0, newValues, 0, min(size_.toDouble(), newValues.size.toDouble()).toInt())
        valueTable = newValues
    }

    fun reverse() {
        var i = 0
        val lastIndex = size_ - 1
        val n = size_ / 2
        while (i < n) {
            val ii = lastIndex - i
            val tempKey = keyTable[i]
            keyTable[i] = keyTable[ii]
            keyTable[ii] = tempKey

            val tempValue = valueTable[i]
            valueTable[i] = valueTable[ii]
            valueTable[ii] = tempValue
            i++
        }
    }

    fun shuffle() {
        for (i in size_ - 1 downTo 0) {
            val ii = random(i)
            val tempKey = keyTable[i]
            keyTable[i] = keyTable[ii]
            keyTable[ii] = tempKey
            val tempValue = valueTable[i]
            valueTable[i] = valueTable[ii]
            valueTable[ii] = tempValue
        }
    }

    /**
     * Reduces the size of the arrays to the specified size. If the arrays are already smaller than the specified size, no action
     * is taken.
     */
    fun truncate(newSize: Int) {
        if (size_ <= newSize) return
        for (i in newSize until size_) {
            keyTable[i] = null
            valueTable[i] = null
        }
        size_ = newSize
    }

    override fun hashCode(): Int {
        val keys = keyTable
        val values = valueTable
        var h = 0
        var i = 0
        val n = size_
        while (i < n) {
            val key: K? = keys[i]
            val value: V? = values[i]
            if (key != null) h += key.hashCode() * 31
            if (value != null) h += value.hashCode()
            i++
        }
        return h
    }


    @Suppress("UNCHECKED_CAST")
    override fun equals(other: Any?): Boolean {
        if (other === this) return true
        if (other !is ArrayMap<*, *>) return false

        other as ArrayMap
        if (other.size_ != size_) return false

        val keys = keyTable
        val values = valueTable
        var i = 0
        val n = size_

        while (i < n) {
            val key = keys[i] as K
            val value = values[i]
            if (value == null) {
                if (other.get(key, dummy as V) != null) {
                    return false
                }
            }
            else {
                if (value != other.get(key)) {
                    return false
                }
            }
            i++
        }
        return true
    }

    /**
     * Uses == for comparison of each value.
     */
    @Suppress("UNCHECKED_CAST")
    fun equalsIdentity(other: Any): Boolean {
        if (other === this) return true
        if (other !is ArrayMap<*, *>) return false

        other as ArrayMap

        if (other.size_ != size_) return false
        val keys = keyTable
        val values = valueTable
        var i = 0
        val n = size_
        while (i < n) {
            if (values[i] !== other.get(keys[i], dummy as V)) return false
            i++
        }
        return true
    }

    override fun toString(): String {
        if (size_ == 0) return "{}"

        val keys = keyTable
        val values = valueTable
        val buffer = StringBuilder(32)
        buffer.append('{')
        buffer.append(keys[0])
        buffer.append('=')
        buffer.append(values[0])

        for (i in 1 until size_) {
            buffer.append(", ")
            buffer.append(keys[i])
            buffer.append('=')
            buffer.append(values[i])
        }
        buffer.append('}')
        return buffer.toString()
    }

    @Suppress("UNCHECKED_CAST")
    override val entries: MutableSet>
        get() = entries() as MutableSet>

    override val keys: MutableSet
        get() = keys()

    override val values: MutableCollection
        get() = values()

    /**
     * Returns an iterator for the entries in the map. Remove is supported.
     *
     * If [Collections.allocateIterators] is false, the same iterator instance is returned each time this method is called.
     * Use the [Entries] constructor for nested or multithreaded iteration.
     *
     * @see Collections.allocateIterators
     */
    @Suppress("UNCHECKED_CAST")
    fun entries(): Entries {
        if (allocateIterators) {
            return Entries(this as ArrayMap)
        }

        if (entries1 == null) {
            entries1 = Entries(this as ArrayMap)
            entries2 = Entries(this as ArrayMap)
        }
        if (!entries1!!.valid) {
            entries1!!.index = 0
            entries1!!.valid = true
            entries2!!.valid = false
            return entries1 as Entries
        }

        entries2!!.index = 0
        entries2!!.valid = true
        entries1!!.valid = false
        return entries2 as Entries
    }

    /**
     * Returns an iterator for the values in the map. Remove is supported.
     *
     * If [Collections.allocateIterators] is false, the same iterator instance is returned each time this method is called.
     * Use the [Entries] constructor for nested or multithreaded iteration.
     *
     * @see Collections.allocateIterators
     */
    @Suppress("UNCHECKED_CAST")
    fun values(): Values {
        if (allocateIterators) {
            return Values(this as ArrayMap)
        }

        if (values1 == null) {
            values1 = Values(this as ArrayMap)
            values2 = Values(this as ArrayMap)
        }
        if (!values1!!.valid) {
            values1!!.index = 0
            values1!!.valid = true
            values2!!.valid = false
            return values1 as Values
        }

        values2!!.index = 0
        values2!!.valid = true
        values1!!.valid = false
        return values2 as Values
    }

    /**
     * Returns an iterator for the keys in the map. Remove is supported.
     *
     * If [Collections.allocateIterators] is false, the same iterator instance is returned each time this method is called.
     * Use the [Entries] constructor for nested or multithreaded iteration.
     *
     * @see Collections.allocateIterators
     */
    @Suppress("UNCHECKED_CAST")
    fun keys(): Keys {
        if (allocateIterators) {
            return Keys(this as ArrayMap)
        }

        if (keys1 == null) {
            keys1 = Keys(this as ArrayMap)
            keys2 = Keys(this as ArrayMap)
        }

        if (!keys1!!.valid) {
            keys1!!.index = 0
            keys1!!.valid = true
            keys2!!.valid = false
            return keys1!!
        }

        keys2!!.index = 0
        keys2!!.valid = true
        keys1!!.valid = false
        return keys2!!
    }

    class Entries(private val map: ArrayMap) : MutableSet>, MutableIterator> {

        private var entry: Entry? = null
        internal var index = 0
        internal var valid = true


        override fun hasNext(): Boolean {
            if (!valid) throw RuntimeException("#iterator() cannot be used nested.")
            return index < map.size_
        }

        override fun add(element: Entry): Boolean {
            map.put(element.key, element.value)
            reset()
            return true
        }

        override fun addAll(elements: Collection>): Boolean {
            var added = false
            elements.forEach { (k,v) ->
                map.put(k, v)
                added = true
            }
            reset()
            return added
        }

        override val size: Int
            get() = map.size_

        override fun clear() {
            map.clear()
        }

        override fun isEmpty(): Boolean {
            return map.isEmpty()
        }

        override fun containsAll(elements: Collection>): Boolean {
            elements.forEach { (k,v) ->
                if (map.get(k) != v) {
                    return false
                }
            }
            return true
        }

        override fun contains(element: Entry): Boolean {
            return map.get(element.key) == element.value
        }

        override fun iterator(): MutableIterator> {
            return this
        }

        override fun retainAll(elements: Collection>): Boolean {
            var removed = false
            map.keyTable.forEach { key ->
                if (key != null) {
                    val hasElement = elements.firstOrNull { it.key == key } != null
                    if (!hasElement) {
                        removed = map.remove(key) != null || removed
                    }
                }
            }
            reset()
            return removed
        }

        override fun removeAll(elements: Collection>): Boolean {
            var removed = false
            elements.forEach { (k,v) ->
                if (map.get(k) == v) {
                    map.removeKey(k)
                    removed = true
                }
            }
            reset()
            return removed
        }

        override fun remove(element: Entry): Boolean {
            if (map.get(element.key) == element.value) {
                map.removeKey(element.key)
                return true
            }
            reset()
            return false
        }

        /** Note the same entry instance is returned each time this method is called.  */
        override fun next(): Entry {
            if (index >= map.size_) throw NoSuchElementException(index.toString())
            if (!valid) throw RuntimeException("#iterator() cannot be used nested.")

            if (entry == null) {
                entry = Entry(map.keyTable[index]!!, map.valueTable[index++], map)
            } else {
                entry!!.key = map.keyTable[index]!!
                entry!!.value = map.valueTable[index++]
            }

            return entry!!
        }

        override fun remove() {
            index--
            map.removeIndex(index)
        }

        fun reset() {
            index = 0
        }
    }

    class Entry(key: K, value: V, val map: ArrayMap) : MutableMap.MutableEntry {
        // we know there will be at least one
        override var key: K = key
        override var value: V? = value

        override fun setValue(newValue: V?): V? {
            val oldValue = value
            map[key] = newValue
            value = newValue
            return oldValue
        }

        override fun toString(): String {
            return "$key=$value"
        }
    }

    class Values(map: ArrayMap) : MutableCollection, MutableIterator {
        private val map: ArrayMap
        var index = 0
        var valid = true

        init {
            this.map = map
        }

        override fun hasNext(): Boolean {
            if (!valid) throw RuntimeException("#iterator() cannot be used nested.")
            return index < map.size_
        }

        override val size: Int
            get() = map.size_

        override fun clear() {
            map.clear()
        }

        override fun addAll(elements: Collection): Boolean {
            throw IllegalStateException("Cannot add values to a map without keys")
        }

        override fun add(element: V): Boolean {
            throw IllegalStateException("Cannot add values to a map without keys")
        }

        override fun isEmpty(): Boolean {
            return map.size_ == 0
        }

        override fun containsAll(elements: Collection): Boolean {
            elements.forEach {
                if (!map.containsValue(it)) {
                    return false
                }
            }
            return true
        }

        override fun contains(element: V): Boolean {
            return map.containsValue(element)
        }

        override fun iterator(): MutableIterator {
            return this
        }

        override fun retainAll(elements: Collection): Boolean {
            var removed = false
            map.keyTable.forEach { key ->
                if (key != null) {
                    val value = map[key]
                    if (!elements.contains(value)) {
                        map.remove(key)
                        removed = true
                    }
                }
            }
            reset()
            return removed
        }

        override fun removeAll(elements: Collection): Boolean {
            var removed = false
            elements.forEach {
                val key = map.getKey(it, false)
                if (key != null) {
                    removed = map.remove(key) != null || removed
                }
            }
            reset()
            return removed
        }

        override fun remove(element: V): Boolean {
            var removed = false
            val key = map.getKey(element, false)
            if (key != null) {
                removed = map.remove(key) != null
            }
            reset()
            return removed
        }

        override fun next(): V {
            if (index >= map.size_) throw NoSuchElementException(index.toString())
            if (!valid) throw RuntimeException("#iterator() cannot be used nested.")

            return map.valueTable[index++]!!
        }

        override fun remove() {
            index--
            map.removeIndex(index)
        }

        fun reset() {
            index = 0
        }

        fun toArray(): ExpandingArray {
            @Suppress("UNCHECKED_CAST")
            return ExpandingArray(true, map.valueTable, index, map.size_ - index) as ExpandingArray
        }

        fun toArray(array: ExpandingArray): ExpandingArray {
            @Suppress("UNCHECKED_CAST")
            array.addAll(map.valueTable as Array, index, map.size_ - index)
            return array
        }
    }

    class Keys(map: ArrayMap) : MutableSet, MutableIterator {
        private val map: ArrayMap
        var index = 0
        var valid = true

        init {
            this.map = map
        }

        override fun hasNext(): Boolean {
            if (!valid) throw RuntimeException("#iterator() cannot be used nested.")
            return index < map.size_
        }

        override fun add(element: K): Boolean {
            throw IllegalStateException("Cannot add keys to a map without values")
        }

        override fun addAll(elements: Collection): Boolean {
            throw IllegalStateException("Cannot add keys to a map without values")
        }

        override val size: Int
            get() = map.size_

        override fun clear() {
            map.clear()
            reset()
        }

        override fun isEmpty(): Boolean {
            return map.size_ == 0
        }

        override fun containsAll(elements: Collection): Boolean {
            elements.forEach {
                if (!map.containsKey(it)) {
                    return false
                }
            }
            return true
        }

        override fun contains(element: K): Boolean {
            return map.containsKey(element)
        }

        override fun iterator(): MutableIterator {
            return this
        }

        override fun retainAll(elements: Collection): Boolean {
            var removed = false
            map.keyTable.forEach {
                if (it != null && !elements.contains(it)) {
                    map.remove(it)
                    removed = true
                }
            }
            reset()
            return removed
        }

        override fun removeAll(elements: Collection): Boolean {
            var removed = false
            elements.forEach {
                if (map.remove(it) == null) {
                    removed = true
                }
            }
            reset()
            return removed
        }

        override fun remove(element: K): Boolean {
            val removed = map.remove(element) == null
            reset()
            return removed
        }

        override fun next(): K {
            if (index >= map.size_) throw NoSuchElementException(index.toString())
            if (!valid) throw RuntimeException("#iterator() cannot be used nested.")

            return map.keyTable[index++]!!
        }

        override fun remove() {
            index--
            map.removeIndex(index)
        }

        fun reset() {
            index = 0
        }

        fun toArray(): ExpandingArray {
            return ExpandingArray(true, map.keyTable, index, map.size_ - index)
        }

        fun toArray(array: ExpandingArray): ExpandingArray {
            @Suppress("UNCHECKED_CAST")
            array.addAll(map.keyTable as Array, index, map.size_ - index)
            return array
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy