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

org.opalj.collection.mutable.FixedSizedHashIDMap.scala Maven / Gradle / Ivy

The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package collection
package mutable

import scala.reflect.ClassTag

/**
 * Conceptually, a map where the keys have to have unique hash codes that are spread over a
 * previously known range.
 *
 * Hence, Using this map has three requirements:
 *  1. The value returned by the hashcode function of the keys have to be unique w.r.t. the
 *     values stored in the map; i.e., two different key objects have to have different
 *     hashcode values.
 *  1. The range of hashcode values returned by the keys has to be known and should be reasonably
 *     consecutive because an array will be preallocated to hold all values.
 *     (it can nevertheless start with an arbitrary int)
 *  1. The number of eventually stored key/values should be > 1/4 of the range of key values to
 *     amortize the costs of the underlying data-structures.
 *
 * @note The `null` key is not permitted.
 * @note This data structure is not thread safe.
 *
 * @author Michael Eichberg
 */
class FixedSizedHashIDMap[K <: AnyRef, V] private (
        private var theKeys:        Array[K],
        private var theValues:      Array[V],
        private var hashCodeOffset: Int // basically -minValue
) { self ⇒

    /**
     * Returns the value stored for the given key.
     *
     * @note If the key is not valid the result is not defined.
     */
    def apply(k: K): V = theValues(k.hashCode() + hashCodeOffset)

    def put(k: K, v: V): this.type = {
        val id = k.hashCode() + hashCodeOffset
        if (id < 0) {
            throw new IllegalArgumentException(s"the key's id (${k.hashCode()}) is < minValue")
        }
        theKeys(id) = k
        theValues(id) = v
        this
    }

    def foreach(f: ((K, V)) ⇒ Unit): Unit = {
        val keys = this.theKeys
        val values = this.theValues
        var i = 0
        val max = keys.length
        while (i < max) {
            val k = keys(i)
            // Recall that all values have to be non-null...
            if (k ne null) f((k, values(i)))
            i += 1
        }
    }

    def iterate(f: (K, V) ⇒ Unit): Unit = {
        val keys = this.theKeys
        val values = this.theValues
        var i = 0
        val max = keys.length
        while (i < max) {
            val k = keys(i)
            // Recall that all values have to be non-null...
            if (k ne null) f(k, values(i))
            i += 1
        }
    }

    def keys: RefIterator[K] = RefIterator.fromNonNullValues(theKeys)

    def entries: RefIterator[(K, V)] = new RefIterator[(K, V)] {
        private[this] def getNextIndex(lastIndex: Int): Int = {
            val keys = self.theKeys
            val max = keys.length
            var i = lastIndex + 1
            while (i < max) { if (keys(i) ne null) { return i; } else i += 1 }
            max
        }
        private[this] var i = getNextIndex(-1)
        def hasNext: Boolean = i < theKeys.length
        def next(): (K, V) = { val r = (theKeys(i), theValues(i)); i = getNextIndex(i); r }
    }

    def mkString(start: String, sep: String, end: String): String = {
        val keys = this.theKeys
        var s = start
        var i = 0
        val max = keys.length
        while (i < max) {
            val k = keys(i)
            if (k ne null) s += s"$k -> ${theValues(i)}"
            i += 1
            while (i < max && (keys(i) eq null)) i += 1
            if ((k ne null) && i < max) s += sep
        }
        s + end
    }

    override def toString: String = mkString("FixedSizedHashMap(", ", ", ")")

}
object FixedSizedHashIDMap {

    def apply[K <: AnyRef: ClassTag, V: ClassTag](
        minValue: Int, // inclusive
        maxValue: Int // inclusive
    ): FixedSizedHashIDMap[K, V] = {
        if (minValue > maxValue) {
            throw new IllegalArgumentException(s"$minValue > $maxValue");
        }

        val length: Long = maxValue.toLong - minValue.toLong + 1L
        if (length > Int.MaxValue.toLong) {
            throw new IllegalArgumentException(s"range to large: [$minValue,$maxValue]");
        }

        val theKeys: Array[K] = new Array(length.toInt)
        val theValues: Array[V] = new Array(length.toInt)
        val hashCodeOffset: Int = -minValue
        new FixedSizedHashIDMap(theKeys, theValues, hashCodeOffset)
    }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy