Code polish

This commit is contained in:
Robinson 2023-08-04 21:16:43 -06:00
parent 2f60a33ef0
commit 599fc0554a
No known key found for this signature in database
GPG Key ID: 8E7DB78588BD6F5C
16 changed files with 400 additions and 172 deletions

View File

@ -231,7 +231,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
* 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 * 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. * does a .equals() comparison of each key in reverse order until the specified key is found.
*/ */
operator fun get(key: K?, defaultValue: V?): V? { operator fun get(key: K?, defaultValue: V): V? {
val keys = keyTable val keys = keyTable
var i = size_ - 1 var i = size_ - 1
if (key == null) { if (key == null) {
@ -246,6 +246,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
i-- i--
} }
} }
return defaultValue return defaultValue
} }
@ -626,7 +627,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
val key = keys[i] as K val key = keys[i] as K
val value = values[i] val value = values[i]
if (value == null) { if (value == null) {
if (other.get(key, dummy as V?) != null) { if (other.get(key, dummy as V) != null) {
return false return false
} }
} }
@ -656,7 +657,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
var i = 0 var i = 0
val n = size_ val n = size_
while (i < n) { while (i < n) {
if (values[i] !== other.get(keys[i], dummy as V?)) return false if (values[i] !== other.get(keys[i], dummy as V)) return false
i++ i++
} }
return true return true
@ -787,11 +788,17 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
return keys2!! return keys2!!
} }
class Entries<K: Any, V>(private val map: ArrayMap<K, V?>) : MutableSet<Entry<K, V?>>,Iterable<Entry<K, V?>>, MutableIterator<Entry<K, V?>> { class Entries<K: Any, V>(private val map: ArrayMap<K, V?>) : MutableSet<Entry<K, V?>>, MutableIterator<Entry<K, V?>> {
var entry: Entry<K, V?> = Entry(map) private lateinit var entry: Entry<K, V?>
var index = 0 internal var index = 0
var valid = true internal var valid = true
init {
if (hasNext()) {
entry = Entry(map)
}
}
override fun hasNext(): Boolean { override fun hasNext(): Boolean {
if (!valid) throw RuntimeException("#iterator() cannot be used nested.") if (!valid) throw RuntimeException("#iterator() cannot be used nested.")
@ -882,7 +889,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
if (index >= map.size_) throw NoSuchElementException(index.toString()) if (index >= map.size_) throw NoSuchElementException(index.toString())
if (!valid) throw RuntimeException("#iterator() cannot be used nested.") if (!valid) throw RuntimeException("#iterator() cannot be used nested.")
entry.key = map.keyTable[index] as K entry.key = map.keyTable[index]!!
entry.value = map.valueTable[index++] entry.value = map.valueTable[index++]
return entry return entry
} }
@ -897,9 +904,10 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
} }
} }
class Entry<K: Any, V>(val map: ArrayMap<K, V?>) : MutableMap.MutableEntry<K, V?> { class Entry<K: Any, V>(val map: ArrayMap<K, V>) : MutableMap.MutableEntry<K, V?> {
override lateinit var key: K // we know there will be at least one
override var value: V? = null override var key: K = map.keyTable[0]!!
override var value: V? = map.valueTable[0]
override fun setValue(newValue: V?): V? { override fun setValue(newValue: V?): V? {
val oldValue = value val oldValue = value
@ -913,7 +921,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
} }
} }
class Values<V>(map: ArrayMap<Any, V?>) : MutableCollection<V>, Iterable<V>, MutableIterator<V> { class Values<V>(map: ArrayMap<Any, V?>) : MutableCollection<V>, MutableIterator<V> {
private val map: ArrayMap<Any, V?> private val map: ArrayMap<Any, V?>
var index = 0 var index = 0
var valid = true var valid = true
@ -1028,7 +1036,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
} }
} }
class Keys<K: Any>(map: ArrayMap<K, Any>) : MutableSet<K>, Iterable<K>, MutableIterator<K> { class Keys<K: Any>(map: ArrayMap<K, Any>) : MutableSet<K>, MutableIterator<K> {
private val map: ArrayMap<K, Any> private val map: ArrayMap<K, Any>
var index = 0 var index = 0
var valid = true var valid = true

View File

@ -238,8 +238,8 @@ open class IntMap<V> : MutableMap<Int, V> {
return if (i >= 0) valueTable[i] else null return if (i >= 0) valueTable[i] else null
} }
operator fun get(key: Int, defaultValue: V?): V? { operator fun get(key: Int, defaultValue: V): V? {
if (key == 0) return if (hasZeroValue) zeroValue else defaultValue if (key == 0) return if (hasZeroValue) zeroValue!! else defaultValue
val i = locateKey(key) val i = locateKey(key)
return if (i >= 0) valueTable[i] else defaultValue return if (i >= 0) valueTable[i] else defaultValue
} }
@ -489,7 +489,7 @@ open class IntMap<V> : MutableMap<Int, V> {
if (key != 0) { if (key != 0) {
val value: V? = valueTable[i] val value: V? = valueTable[i]
if (value == null) { if (value == null) {
if (other.get(key, ObjectMap.dummy as V?) != null) return false if (other.get(key, ObjectMap.dummy as V) != null) return false
} }
else { else {
if (value != other[key]) return false if (value != other[key]) return false
@ -518,7 +518,7 @@ open class IntMap<V> : MutableMap<Int, V> {
val n = keyTable.size val n = keyTable.size
while (i < n) { while (i < n) {
val key = keyTable[i] val key = keyTable[i]
if (key != 0 && valueTable[i] !== other.get(key, ObjectMap.dummy as V?)) return false if (key != 0 && valueTable[i] !== other.get(key, ObjectMap.dummy as V)) return false
i++ i++
} }
return true return true

View File

@ -263,16 +263,13 @@ class LockFreeBiMap<K: Any, V: Any> : MutableMap<K, V>, Cloneable, Serializable
* *
* @param key key whose mapping is to be removed from the map * @param key key whose mapping is to be removed from the map
* *
* @return the previous value associated with <tt>key</tt>, or * @return the previous value associated with [key]
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/ */
@Synchronized @Synchronized
override fun remove(key: K): V? { override fun remove(key: K): V {
val value = forwardHashMap.remove(key) val value = forwardHashMap.remove(key)
reverseHashMap.remove(value) reverseHashMap.remove(value)
return value return value!!
} }
/** /**

View File

@ -34,10 +34,10 @@ import java.util.concurrent.atomic.*
* *
* This data structure is for many-read/few-write scenarios * This data structure is for many-read/few-write scenarios
*/ */
class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable { class LockFreeHashMap<K: Any, V> : MutableMap<K, V>, Cloneable, Serializable {
@Volatile @Volatile
private var hashMap: MutableMap<K, V?> private var hashMap: HashMap<K, V>
// synchronized is used here to ensure the "single writer principle", and make sure that ONLY one thread at a time can enter this // synchronized is used here to ensure the "single writer principle", and make sure that ONLY one thread at a time can enter this
@ -96,11 +96,11 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
hashMap = HashMap(initialCapacity, loadFactor) hashMap = HashMap(initialCapacity, loadFactor)
} }
val map: MutableMap<K, V> private val map: MutableMap<K, V>
get() { get() {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return mapREF[this] as MutableMap<K, V> return mapREF[this] as MutableMap<K, V>
} }
override val size: Int override val size: Int
@ -114,16 +114,14 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
return map.keys return map.keys
} }
override val values: MutableCollection<V?> override val values: MutableCollection<V>
get() { get() {
@Suppress("UNCHECKED_CAST") return map.values
return map.values as MutableCollection<V?>
} }
override val entries: MutableSet<MutableMap.MutableEntry<K, V?>> override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() { get() {
@Suppress("UNCHECKED_CAST") return map.entries
return map.entries as MutableSet<MutableMap.MutableEntry<K, V?>>
} }
override fun isEmpty(): Boolean { override fun isEmpty(): Boolean {
@ -136,7 +134,7 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
return mapREF[this].containsKey(key) return mapREF[this].containsKey(key)
} }
override fun containsValue(value: V?): Boolean { override fun containsValue(value: V): Boolean {
// use the SWP to get a lock-free get of the value // use the SWP to get a lock-free get of the value
return mapREF[this].containsValue(value) return mapREF[this].containsValue(value)
} }
@ -147,7 +145,7 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
} }
@Synchronized @Synchronized
override fun put(key: K, value: V?): V? { override fun put(key: K, value: V): V? {
return hashMap.put(key, value) return hashMap.put(key, value)
} }
@ -168,7 +166,7 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
} }
@Synchronized @Synchronized
override fun putAll(from: Map<out K, V?>) { override fun putAll(from: Map<out K, V>) {
hashMap.putAll(from) hashMap.putAll(from)
} }

View File

@ -282,10 +282,7 @@ class LockFreeIntBiMap<V: Any> : MutableMap<Int, V>, Cloneable, Serializable {
* *
* @param key key whose mapping is to be removed from the map * @param key key whose mapping is to be removed from the map
* *
* @return the previous value associated with <tt>key</tt>, or * @return the previous value associated with [key]
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/ */
@Synchronized @Synchronized
override fun remove(key: Int): V? { override fun remove(key: Int): V? {

View File

@ -47,7 +47,7 @@ import java.util.concurrent.atomic.*
* Iteration can be very slow for a map with a large capacity. [.clear] and [.shrink] can be used to reduce * Iteration can be very slow for a map with a large capacity. [.clear] and [.shrink] can be used to reduce
* the capacity. [OrderedMap] provides much faster iteration. * the capacity. [OrderedMap] provides much faster iteration.
*/ */
class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable { class LockFreeIntMap<V> : MutableMap<Int, V>, Cloneable, Serializable {
@Volatile @Volatile
private var hashMap: IntMap<V> private var hashMap: IntMap<V>
@ -107,7 +107,11 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
return value.containsKey(key) return value.containsKey(key)
} }
override fun containsValue(value: Any?, identity: Boolean): Boolean { override fun containsValue(value: V): Boolean {
return containsValue(value, false)
}
fun containsValue(value: Any?, identity: Boolean): Boolean {
// use the SWP to get a lock-free get of the value // use the SWP to get a lock-free get of the value
return mapREF[this].containsValue(value, identity) return mapREF[this].containsValue(value, identity)
} }
@ -130,7 +134,7 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
} }
@Synchronized @Synchronized
override fun putAll(from: IntMap<out V>) { override fun putAll(from: Map<out Int, V>) {
hashMap.putAll(from) hashMap.putAll(from)
} }
@ -145,9 +149,10 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
* Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each * Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration. * time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/ */
override fun keys(): Keys { override val keys: IntMap.Keys
return mapREF[this].keys() get() {
} return mapREF[this].keys()
}
/** /**
* DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility! * DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility!
@ -156,9 +161,10 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration. * time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun values(): Values<V> { override val values: IntMap.Values<V>
return mapREF[this].values() as Values<V> get() {
} return mapREF[this].values() as IntMap.Values<V>
}
/** /**
* DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility! * DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility!
@ -167,15 +173,16 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration. * time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun entries(): Entries<V?> { override val entries: MutableSet<MutableMap.MutableEntry<Int, V>>
return mapREF[this].entries() as Entries<V?> get() {
} return mapREF[this].entries() as MutableSet<MutableMap.MutableEntry<Int, V>>
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
return mapREF[this] == other return mapREF[this] == other
} }
override fun equalsIdentity(other: Any?): Boolean { fun equalsIdentity(other: Any?): Boolean {
return mapREF[this].equalsIdentity(other) return mapREF[this].equalsIdentity(other)
} }
@ -192,7 +199,7 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
* is done by allocating new arrays, though for large arrays this can be faster than clearing the existing array. * is done by allocating new arrays, though for large arrays this can be faster than clearing the existing array.
*/ */
@Synchronized @Synchronized
override fun clear(maximumCapacity: Int) { fun clear(maximumCapacity: Int) {
mapREF[this].clear(maximumCapacity) mapREF[this].clear(maximumCapacity)
} }
@ -202,7 +209,7 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
* If the map contains more items than the specified capacity, the next highest power of two capacity is used instead. * If the map contains more items than the specified capacity, the next highest power of two capacity is used instead.
*/ */
@Synchronized @Synchronized
override fun shrink(maximumCapacity: Int) { fun shrink(maximumCapacity: Int) {
mapREF[this].shrink(maximumCapacity) mapREF[this].shrink(maximumCapacity)
} }

View File

@ -36,10 +36,10 @@ import java.util.concurrent.atomic.*
*/ */
class LockFreeObjectBiMap<K: Any, V: Any> : MutableMap<K, V>, Cloneable, Serializable { class LockFreeObjectBiMap<K: Any, V: Any> : MutableMap<K, V>, Cloneable, Serializable {
@Volatile @Volatile
private var forwardHashMap: MutableMap<K, V> private var forwardHashMap: ObjectMap<K, V>
@Volatile @Volatile
private var reverseHashMap: MutableMap<V, K> private var reverseHashMap: ObjectMap<V, K>
private val inverse: LockFreeObjectBiMap<V, K> private val inverse: LockFreeObjectBiMap<V, K>
@ -52,7 +52,7 @@ class LockFreeObjectBiMap<K: Any, V: Any> : MutableMap<K, V>, Cloneable, Seriali
inverse = LockFreeObjectBiMap(reverseHashMap, forwardHashMap, this) inverse = LockFreeObjectBiMap(reverseHashMap, forwardHashMap, this)
} }
private constructor(forwardHashMap: MutableMap<K, V>, reverseHashMap: MutableMap<V, K>, inverse: LockFreeObjectBiMap<V, K>) { private constructor(forwardHashMap: ObjectMap<K, V>, reverseHashMap: ObjectMap<V, K>, inverse: LockFreeObjectBiMap<V, K>) {
this.forwardHashMap = forwardHashMap this.forwardHashMap = forwardHashMap
this.reverseHashMap = reverseHashMap this.reverseHashMap = reverseHashMap
this.inverse = inverse this.inverse = inverse
@ -428,10 +428,10 @@ class LockFreeObjectBiMap<K: Any, V: Any> : MutableMap<K, V>, Cloneable, Seriali
// Recommended for best performance while adhering to the "single writer principle". Must be static-final // Recommended for best performance while adhering to the "single writer principle". Must be static-final
private val forwardREF = AtomicReferenceFieldUpdater.newUpdater( private val forwardREF = AtomicReferenceFieldUpdater.newUpdater(
LockFreeObjectBiMap::class.java, MutableMap::class.java, "forwardHashMap" LockFreeObjectBiMap::class.java, ObjectMap::class.java, "forwardHashMap"
) )
private val reverseREF = AtomicReferenceFieldUpdater.newUpdater( private val reverseREF = AtomicReferenceFieldUpdater.newUpdater(
LockFreeObjectBiMap::class.java, MutableMap::class.java, "reverseHashMap" LockFreeObjectBiMap::class.java, ObjectMap::class.java, "reverseHashMap"
) )
} }
} }

View File

@ -286,21 +286,19 @@ class LockFreeObjectIntBiMap<K: Any> : MutableMap<K, Int>, Cloneable, Serializab
* *
* @param key key whose mapping is to be removed from the map * @param key key whose mapping is to be removed from the map
* *
* @return the previous value associated with <tt>key</tt>, or * @return the previous value associated with [key] or [defaultReturnValue] if it doesn't exist
* <tt>null</tt> if there was no mapping for <tt>key</tt>. *
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/ */
@Synchronized @Synchronized
override fun remove(key: K): Int? { override fun remove(key: K): Int {
val value = forwardHashMap.remove(key) val value = forwardHashMap.remove(key)
reverseHashMap.remove(value) reverseHashMap.remove(value)
return value return value ?: defaultReturnValue
} }
/** /**
* Returns the value to which the specified key is mapped, * Returns the value to which the specified key is mapped,
* or `null` if this map contains no mapping for the key. * or [defaultReturnValue] if this map contains no mapping for the key.
* *
* *
* More formally, if this map contains a mapping from a key * More formally, if this map contains a mapping from a key
@ -309,9 +307,9 @@ class LockFreeObjectIntBiMap<K: Any> : MutableMap<K, Int>, Cloneable, Serializab
* it returns `null`. (There can be at most one such mapping.) * it returns `null`. (There can be at most one such mapping.)
* *
* *
* A return value of `null` does not *necessarily* * A return value of [defaultReturnValue] does not *necessarily*
* indicate that the map contains no mapping for the key; it's also * indicate that the map contains no mapping for the key; it's also
* possible that the map explicitly maps the key to `null`. * possible that the map explicitly maps the key to [defaultReturnValue].
* The [containsKey][HashMap.containsKey] operation may be used to * The [containsKey][HashMap.containsKey] operation may be used to
* distinguish these two cases. * distinguish these two cases.
* *
@ -319,8 +317,7 @@ class LockFreeObjectIntBiMap<K: Any> : MutableMap<K, Int>, Cloneable, Serializab
*/ */
override operator fun get(key: K): Int { override operator fun get(key: K): Int {
// use the SWP to get a lock-free get of the value // use the SWP to get a lock-free get of the value
@Suppress("UNCHECKED_CAST") return forwardREF[this][key] ?: defaultReturnValue
return (forwardREF[this] as ObjectIntMap<K>)[key] as Int
} }
/** /**

View File

@ -0,0 +1,221 @@
/*
* 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.
*/
package dorkbox.collections
import java.io.Serializable
import java.util.concurrent.atomic.*
/**
* This class uses the "single-writer-principle" for lock-free publication.
*
*
* Since there are only 2 methods to guarantee that modifications can only be called one-at-a-time (either it is only called by
* one thread, or only one thread can access it at a time) -- we chose the 2nd option -- and use 'synchronized' to make sure that only
* one thread can access this modification methods at a time. Getting or checking the presence of values can then happen in a lock-free
* manner.
*
*
* According to my benchmarks, this is approximately 25% faster than ConcurrentHashMap for (all types of) reads, and a lot slower for
* contended writes.
*
*
* This data structure is for many-read/few-write scenarios
*
*
* An unordered map. This implementation is a cuckoo hash map using 3 hashes, random walking, and a small stash for problematic
* keys. Null keys are not allowed. Null values are allowed. No allocation is done except when growing the table size. <br></br>
*
*
* This map performs very fast get, containsKey, and remove (typically O(1), worst case O(log(n))). Put may be a bit slower,
* depending on hash collisions. Load factors greater than 0.91 greatly increase the chances the map will have to rehash to the
* next higher POT size.
*
*
* Iteration can be very slow for a map with a large capacity. [.clear] and [.shrink] can be used to reduce
* the capacity. [OrderedMap] provides much faster iteration.
*/
class LockFreeObjectIntMap<K: Any> : MutableMap<K, Int>, Cloneable, Serializable {
@Volatile
private var hashMap: ObjectIntMap<K>
// synchronized is used here to ensure the "single writer principle", and make sure that ONLY one thread at a time can enter this
// section. Because of this, we can have unlimited reader threads all going at the same time, without contention (which is our
// use-case 99% of the time)
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
constructor() {
hashMap = ObjectIntMap()
}
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity.
*
* @throws IllegalArgumentException if the initial capacity is negative.
*/
constructor(initialCapacity: Int) {
hashMap = ObjectIntMap(initialCapacity)
}
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
*
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
constructor(initialCapacity: Int, loadFactor: Float) {
hashMap = ObjectIntMap(initialCapacity, loadFactor)
}
override val size: Int
get() {
// use the SWP to get a lock-free get of the value
return mapREF[this].size
}
override fun isEmpty(): Boolean {
// use the SWP to get a lock-free get of the value
return mapREF[this].size == 0
}
override fun containsKey(key: K): Boolean {
// use the SWP to get a lock-free get of the value
return mapREF[this].containsKey(key)
}
override fun containsValue(value: Int): Boolean {
// use the SWP to get a lock-free get of the value
return mapREF[this].containsValue(value)
}
override operator fun get(key: K): Int? {
// use the SWP to get a lock-free get of the value
return mapREF[this].get(key)
}
/**
* Returns the value for the specified key, or the default value if the key is not in the map.
*/
operator fun get(key: K, defaultValue: Int): Int {
@Suppress("UNCHECKED_CAST")
val objectIntMap = mapREF[this] as ObjectIntMap<K>
return objectIntMap.get(key, defaultValue)
}
@Synchronized
override fun put(key: K, value: Int): Int? {
return hashMap.put(key, value)
}
@Synchronized
override fun remove(key: K): Int? {
return hashMap.remove(key)
}
@Synchronized
override fun putAll(from: Map<out K, Int>) {
hashMap.putAll(from)
}
@Synchronized
override fun clear() {
hashMap.clear()
}
/**
* DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility!
*
* Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/
@Suppress("UNCHECKED_CAST")
override val keys: ObjectIntMap.Keys<K>
get() {
return mapREF[this].keys() as ObjectIntMap.Keys<K>
}
/**
* DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility!
*
* Returns an iterator for the values in the map. Remove is supported. Note that the same iterator instance is returned each
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/
override val values: ObjectIntMap.Values
get() {
return mapREF[this].values()
}
/**
* DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility!
*
* Returns an iterator for the entries in the map. Remove is supported. Note that the same iterator instance is returned each
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/
@Suppress("UNCHECKED_CAST")
override val entries: MutableSet<MutableMap.MutableEntry<K, Int>>
get() {
return mapREF[this].entries() as MutableSet<MutableMap.MutableEntry<K, Int>>
}
override fun equals(other: Any?): Boolean {
return mapREF[this] == other
}
override fun hashCode(): Int {
return mapREF[this].hashCode()
}
override fun toString(): String {
return mapREF[this].toString()
}
/**
* Clears the map and reduces the size of the backing arrays to be the specified capacity, if they are larger. The reduction
* is done by allocating new arrays, though for large arrays this can be faster than clearing the existing array.
*/
@Synchronized
fun clear(maximumCapacity: Int) {
mapREF[this].clear(maximumCapacity)
}
/**
* Reduces the size of the backing arrays to be the specified capacity or less. If the capacity is already less, nothing is
* done.
* If the map contains more items than the specified capacity, the next highest power of two capacity is used instead.
*/
@Synchronized
fun shrink(maximumCapacity: Int) {
mapREF[this].shrink(maximumCapacity)
}
companion object {
const val version = Collections.version
// Recommended for best performance while adhering to the "single writer principle". Must be static-final
private val mapREF = AtomicReferenceFieldUpdater.newUpdater(
LockFreeObjectIntMap::class.java, ObjectIntMap::class.java, "hashMap"
)
}
}

View File

@ -47,10 +47,10 @@ import java.util.concurrent.atomic.*
* Iteration can be very slow for a map with a large capacity. [.clear] and [.shrink] can be used to reduce * Iteration can be very slow for a map with a large capacity. [.clear] and [.shrink] can be used to reduce
* the capacity. [OrderedMap] provides much faster iteration. * the capacity. [OrderedMap] provides much faster iteration.
*/ */
class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable { class LockFreeObjectMap<K: Any, V> : MutableMap<K, V>, Cloneable, Serializable {
@Volatile @Volatile
private var hashMap: ObjectMap<K, V?> private var hashMap: ObjectMap<K, V>
// synchronized is used here to ensure the "single writer principle", and make sure that ONLY one thread at a time can enter this // synchronized is used here to ensure the "single writer principle", and make sure that ONLY one thread at a time can enter this
// section. Because of this, we can have unlimited reader threads all going at the same time, without contention (which is our // section. Because of this, we can have unlimited reader threads all going at the same time, without contention (which is our
@ -107,7 +107,12 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
return value.containsKey(key) return value.containsKey(key)
} }
override fun containsValue(value: Any?, identity: Boolean): Boolean { override fun containsValue(value: V): Boolean {
// use the SWP to get a lock-free get of the value
return mapREF[this].containsValue(value, false)
}
fun containsValue(value: Any?, identity: Boolean): Boolean {
// use the SWP to get a lock-free get of the value // use the SWP to get a lock-free get of the value
return mapREF[this].containsValue(value, identity) return mapREF[this].containsValue(value, identity)
} }
@ -120,7 +125,7 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
} }
@Synchronized @Synchronized
override fun put(key: K, value: V?): V? { override fun put(key: K, value: V): V? {
return hashMap.put(key, value) return hashMap.put(key, value)
} }
@ -130,7 +135,7 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
} }
@Synchronized @Synchronized
override fun putAll(from: ObjectMap<out K, out V?>) { override fun putAll(from: Map<out K, V>) {
hashMap.putAll(from) hashMap.putAll(from)
} }
@ -139,6 +144,8 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
hashMap.clear() hashMap.clear()
} }
/** /**
* DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility! * DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility!
* *
@ -146,9 +153,10 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration. * time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun keys(): Keys<K> { override val keys: ObjectMap.Keys<K>
return mapREF[this].keys() as Keys<K> get() {
} return mapREF[this].keys() as ObjectMap.Keys<K>
}
/** /**
* DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility! * DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility!
@ -157,9 +165,10 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration. * time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun values(): Values<V?> { override val values: ObjectMap.Values<V>
return mapREF[this].values() as Values<V?> get() {
} return mapREF[this].values() as ObjectMap.Values<V>
}
/** /**
* DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility! * DO NOT MODIFY THE MAP VIA THIS (unless you synchronize around it!) It will result in unknown object visibility!
@ -168,15 +177,16 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration. * time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun entries(): Entries<K, V?> { override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
return mapREF[this].entries() as Entries<K, V?> get() {
} return mapREF[this].entries() as MutableSet<MutableMap.MutableEntry<K, V>>
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
return mapREF[this] == other return mapREF[this] == other
} }
override fun equalsIdentity(other: Any?): Boolean { fun equalsIdentity(other: Any?): Boolean {
return mapREF[this].equalsIdentity(other) return mapREF[this].equalsIdentity(other)
} }
@ -193,7 +203,7 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
* is done by allocating new arrays, though for large arrays this can be faster than clearing the existing array. * is done by allocating new arrays, though for large arrays this can be faster than clearing the existing array.
*/ */
@Synchronized @Synchronized
override fun clear(maximumCapacity: Int) { fun clear(maximumCapacity: Int) {
mapREF[this].clear(maximumCapacity) mapREF[this].clear(maximumCapacity)
} }
@ -203,7 +213,7 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
* If the map contains more items than the specified capacity, the next highest power of two capacity is used instead. * If the map contains more items than the specified capacity, the next highest power of two capacity is used instead.
*/ */
@Synchronized @Synchronized
override fun shrink(maximumCapacity: Int) { fun shrink(maximumCapacity: Int) {
mapREF[this].shrink(maximumCapacity) mapREF[this].shrink(maximumCapacity)
} }

View File

@ -54,7 +54,7 @@ import java.util.*
* @author Nathan Sweet * @author Nathan Sweet
* @author Tommy Ettinger * @author Tommy Ettinger
*/ */
class LongMap<V> : MutableMap<Long, V> { class LongMap<V> : MutableMap<Long, V?> {
companion object { companion object {
const val version = Collections.version const val version = Collections.version
} }
@ -87,16 +87,16 @@ class LongMap<V> : MutableMap<Long, V> {
protected var mask: Int protected var mask: Int
@Transient @Transient
private var entries1: Entries<V>? = null private var entries1: Entries<V?>? = null
@Transient @Transient
private var entries2: Entries<V>? = null private var entries2: Entries<V?>? = null
@Transient @Transient
private var values1: Values<V>? = null private var values1: Values<V?>? = null
@Transient @Transient
private var values2: Values<V>? = null private var values2: Values<V?>? = null
@Transient @Transient
private var keys1: Keys? = null private var keys1: Keys? = null
@ -175,7 +175,7 @@ class LongMap<V> : MutableMap<Long, V> {
} }
override fun put(key: Long, value: V): V? { override fun put(key: Long, value: V?): V? {
if (key == 0L) { if (key == 0L) {
val oldValue = zeroValue val oldValue = zeroValue
zeroValue = value zeroValue = value
@ -198,7 +198,7 @@ class LongMap<V> : MutableMap<Long, V> {
return null return null
} }
fun putAll(map: LongMap<out V>) { fun putAll(map: LongMap<out V?>) {
ensureCapacity(map.size_) ensureCapacity(map.size_)
if (map.hasZeroValue) { if (map.hasZeroValue) {
put(0, map.zeroValue!!) put(0, map.zeroValue!!)
@ -237,8 +237,8 @@ class LongMap<V> : MutableMap<Long, V> {
return if (i >= 0) valueTable[i] else null return if (i >= 0) valueTable[i] else null
} }
operator fun get(key: Long, defaultValue: V?): V? { operator fun get(key: Long, defaultValue: V): V? {
if (key == 0L) return if (hasZeroValue) zeroValue else defaultValue if (key == 0L) return if (hasZeroValue) zeroValue!! else defaultValue
val i = locateKey(key) val i = locateKey(key)
return if (i >= 0) valueTable[i] else defaultValue return if (i >= 0) valueTable[i] else defaultValue
} }
@ -294,7 +294,7 @@ class LongMap<V> : MutableMap<Long, V> {
return size_ == 0 return size_ == 0
} }
override fun putAll(from: Map<out Long, V>) { override fun putAll(from: Map<out Long, V?>) {
ensureCapacity(from.size) ensureCapacity(from.size)
from.entries.forEach { (k,v) -> from.entries.forEach { (k,v) ->
put(k, v) put(k, v)
@ -328,13 +328,13 @@ class LongMap<V> : MutableMap<Long, V> {
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override val entries: MutableSet<MutableMap.MutableEntry<Long, V>> override val entries: MutableSet<MutableMap.MutableEntry<Long, V?>>
get() = entries() as MutableSet<MutableMap.MutableEntry<Long, V>> get() = entries() as MutableSet<MutableMap.MutableEntry<Long, V?>>
override val keys: MutableSet<Long> override val keys: MutableSet<Long>
get() = keys() get() = keys()
override val size: Int override val size: Int
get() = size_ get() = size_
override val values: MutableCollection<V> override val values: MutableCollection<V?>
get() = values() get() = values()
override fun clear() { override fun clear() {
@ -346,7 +346,7 @@ class LongMap<V> : MutableMap<Long, V> {
hasZeroValue = false hasZeroValue = false
} }
override fun containsValue(value: V): Boolean { override fun containsValue(value: V?): Boolean {
return containsValue(value, false) return containsValue(value, false)
} }
@ -488,7 +488,7 @@ class LongMap<V> : MutableMap<Long, V> {
if (key != 0L) { if (key != 0L) {
val value: V? = valueTable[i] val value: V? = valueTable[i]
if (value == null) { if (value == null) {
if (other.get(key, ObjectMap.dummy as V?) != null) return false if (other.get(key, ObjectMap.dummy as V) != null) return false
} }
else { else {
if (value != other[key]) return false if (value != other[key]) return false
@ -517,7 +517,7 @@ class LongMap<V> : MutableMap<Long, V> {
val n = keyTable.size val n = keyTable.size
while (i < n) { while (i < n) {
val key = keyTable[i] val key = keyTable[i]
if (key != 0L && valueTable[i] !== other.get(key, ObjectMap.dummy as V?)) return false if (key != 0L && valueTable[i] !== other.get(key, ObjectMap.dummy as V)) return false
i++ i++
} }
return true return true
@ -588,22 +588,23 @@ class LongMap<V> : MutableMap<Long, V> {
* If [Collections.allocateIterators] is false, the same iterator instance is returned each time this method is called. * 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. * Use the [Entries] constructor for nested or multithreaded iteration.
*/ */
fun values(): Values<V> { @Suppress("UNCHECKED_CAST")
if (allocateIterators) return Values(this) fun values(): Values<V?> {
if (allocateIterators) return Values(this as LongMap<V?>)
if (values1 == null) { if (values1 == null) {
values1 = Values(this) values1 = Values(this as LongMap<V?>)
values2 = Values(this) values2 = Values(this as LongMap<V?>)
} }
if (!values1!!.valid) { if (!values1!!.valid) {
values1!!.reset() values1!!.reset()
values1!!.valid = true values1!!.valid = true
values2!!.valid = false values2!!.valid = false
return values1 as Values<V> return values1 as Values<V?>
} }
values2!!.reset() values2!!.reset()
values2!!.valid = true values2!!.valid = true
values1!!.valid = false values1!!.valid = false
return values2 as Values<V> return values2 as Values<V?>
} }
/** /**

View File

@ -255,7 +255,7 @@ open class ObjectIntMap<K: Any> : MutableMap<K, Int> {
/** /**
* Returns the value for the specified key, or the default value if the key is not in the map. * Returns the value for the specified key, or the default value if the key is not in the map.
*/ */
operator fun get(key: K, defaultValue: Int?): Int? { open operator fun get(key: K, defaultValue: Int): Int {
val i = locateKey(key) val i = locateKey(key)
return if (i < 0) { return if (i < 0) {
defaultValue defaultValue

View File

@ -59,7 +59,7 @@ import java.util.*
* @author Nathan Sweet * @author Nathan Sweet
* @author Tommy Ettinger * @author Tommy Ettinger
*/ */
open class ObjectMap<K: Any, V> : MutableMap<K, V> { open class ObjectMap<K: Any, V> : MutableMap<K, V?> {
companion object { companion object {
const val version = Collections.version const val version = Collections.version
@ -189,7 +189,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
/** /**
* Returns the old value associated with the specified key, or null. * Returns the old value associated with the specified key, or null.
*/ */
override fun put(key: K, value: V): V? { override fun put(key: K, value: V?): V? {
var i = locateKey(key) var i = locateKey(key)
if (i >= 0) { // Existing key was found. if (i >= 0) { // Existing key was found.
val oldValue = valueTable[i] val oldValue = valueTable[i]
@ -203,7 +203,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
return null return null
} }
open fun putAll(from: ObjectMap<out K, out V>) { open fun putAll(from: ObjectMap<out K, out V?>) {
ensureCapacity(from.mapSize) ensureCapacity(from.mapSize)
val keyTable = from.keyTable val keyTable = from.keyTable
@ -214,13 +214,13 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
while (i < n) { while (i < n) {
key = keyTable[i] key = keyTable[i]
if (key != null) { if (key != null) {
put(key, valueTable[i]!!) put(key, valueTable[i])
} }
i++ i++
} }
} }
override fun putAll(from: Map<out K, V>) { override fun putAll(from: Map<out K, V?>) {
ensureCapacity(from.size) ensureCapacity(from.size)
from.forEach { (k, v) -> from.forEach { (k, v) ->
@ -255,7 +255,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
/** /**
* Returns the value for the specified key, or the default value if the key is not in the map. * Returns the value for the specified key, or the default value if the key is not in the map.
*/ */
operator fun get(key: K, defaultValue: V?): V? { operator fun get(key: K, defaultValue: V): V? {
val i = locateKey(key) val i = locateKey(key)
return if (i < 0) { return if (i < 0) {
defaultValue defaultValue
@ -338,7 +338,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
Arrays.fill(valueTable, null) Arrays.fill(valueTable, null)
} }
override fun containsValue(value: V): Boolean { override fun containsValue(value: V?): Boolean {
return containsValue(value, false) return containsValue(value, false)
} }
@ -452,7 +452,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
if (key != null) { if (key != null) {
val value = valueTable[i] val value = valueTable[i]
if (value == null) { if (value == null) {
if (other.get(key, dummy as V?) != null) return false if (other.get(key, dummy as V) != null) return false
} }
else { else {
if (value != other.get(key)) return false if (value != other.get(key)) return false
@ -478,7 +478,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
val n = keyTable.size val n = keyTable.size
while (i < n) { while (i < n) {
val key: K? = keyTable[i] val key: K? = keyTable[i]
if (key != null && valueTable[i] !== other.get(key, dummy as V?)) return false if (key != null && valueTable[i] !== other.get(key, dummy as V)) return false
i++ i++
} }
return true return true
@ -523,8 +523,8 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
return buffer.toString() return buffer.toString()
} }
override val entries: MutableSet<MutableMap.MutableEntry<K, V>> override val entries: MutableSet<MutableMap.MutableEntry<K, V?>>
get() = entries() as MutableSet<MutableMap.MutableEntry<K, V>> get() = entries() as MutableSet<MutableMap.MutableEntry<K, V?>>
/** /**
@ -553,7 +553,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
return entries2 as Entries<K, V?> return entries2 as Entries<K, V?>
} }
override val values: MutableCollection<V> override val values: MutableCollection<V?>
get() = values() get() = values()
/** /**
@ -563,7 +563,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
* *
* Use the [Values] constructor for nested or multithreaded iteration. * Use the [Values] constructor for nested or multithreaded iteration.
*/ */
open fun values(): Values<V> { open fun values(): Values<V?> {
if (allocateIterators) return Values(this as ObjectMap<K, V?>) if (allocateIterators) return Values(this as ObjectMap<K, V?>)
if (values1 == null) { if (values1 == null) {
values1 = Values(this as ObjectMap<K, V?>) values1 = Values(this as ObjectMap<K, V?>)
@ -573,12 +573,12 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
values1!!.reset() values1!!.reset()
values1!!.valid = true values1!!.valid = true
values2!!.valid = false values2!!.valid = false
return values1 as Values<V> return values1 as Values<V?>
} }
values2!!.reset() values2!!.reset()
values2!!.valid = true values2!!.valid = true
values1!!.valid = false values1!!.valid = false
return values2 as Values<V> return values2 as Values<V?>
} }
override val keys: MutableSet<K> override val keys: MutableSet<K>
@ -601,17 +601,18 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
keys1!!.reset() keys1!!.reset()
keys1!!.valid = true keys1!!.valid = true
keys2!!.valid = false keys2!!.valid = false
return keys1 as Keys<K> return keys1!!
} }
keys2!!.reset() keys2!!.reset()
keys2!!.valid = true keys2!!.valid = true
keys1!!.valid = false keys1!!.valid = false
return keys2 as Keys<K> return keys2!!
} }
class Entry<K: Any, V>(val map: ObjectMap<K, V?>) : MutableMap.MutableEntry<K, V?> { class Entry<K: Any, V>(val map: ObjectMap<K, V?>, index: Int) : MutableMap.MutableEntry<K, V?> {
override lateinit var key: K // we know there will be at least one
override var value: V? = null override var key: K = map.keyTable[index]!!
override var value: V? = map.valueTable[index]
override fun setValue(newValue: V?): V? { override fun setValue(newValue: V?): V? {
val oldValue = value val oldValue = value
@ -625,7 +626,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
} }
} }
abstract class MapIterator<K: Any, V, I>(val map: ObjectMap<K, V>) : Iterable<I>, MutableIterator<I> { abstract class MapIterator<K: Any, V, I>(val map: ObjectMap<K, V?>) : MutableIterator<I> {
var hasNext = false var hasNext = false
var nextIndex = 0 var nextIndex = 0
var currentIndex = 0 var currentIndex = 0
@ -683,7 +684,14 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
} }
open class Entries<K: Any, V>(map: ObjectMap<K, V?>) : MutableSet<Entry<K, V?>>, MapIterator<K, V?, Entry<K, V?>>(map) { open class Entries<K: Any, V>(map: ObjectMap<K, V?>) : MutableSet<Entry<K, V?>>, MapIterator<K, V?, Entry<K, V?>>(map) {
var entry = Entry<K, V?>(map) internal lateinit var entry: Entry<K, V?>
init {
if (hasNext) {
findNextIndex()
entry = Entry(map, nextIndex)
}
}
/** Note the same entry instance is returned each time this method is called. */ /** Note the same entry instance is returned each time this method is called. */
override fun next(): Entry<K, V?> { override fun next(): Entry<K, V?> {
@ -776,7 +784,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
} }
} }
open class Values<V>(map: ObjectMap<*, V?>) : MutableCollection<V>, MapIterator<Any, V, V>(map as ObjectMap<Any, V>) { open class Values<V>(map: ObjectMap<*, V?>) : MutableCollection<V>, MapIterator<Any, V, V>(map as ObjectMap<Any, V?>) {
override fun hasNext(): Boolean { override fun hasNext(): Boolean {
if (!valid) throw RuntimeException("#iterator() cannot be used nested.") if (!valid) throw RuntimeException("#iterator() cannot be used nested.")
return hasNext return hasNext

View File

@ -63,7 +63,7 @@ import dorkbox.collections.Collections.allocateIterators
* @author Nathan Sweet * @author Nathan Sweet
* @author Tommy Ettinger * @author Tommy Ettinger
*/ */
class OrderedMap<K, V> : ObjectMap<K, V?> where K : Any, K : Comparable<K> { class OrderedMap<K, V> : ObjectMap<K, V> where K : Any, K : Comparable<K> {
companion object { companion object {
const val version = Collections.version const val version = Collections.version
} }
@ -132,14 +132,14 @@ class OrderedMap<K, V> : ObjectMap<K, V?> where K : Any, K : Comparable<K> {
return null return null
} }
fun putAll(map: OrderedMap<K, out V?>) { fun putAll(map: OrderedMap<K, V>) {
ensureCapacity(map.size) ensureCapacity(map.size)
val keys = map.keys_ val keys = map.keys_
var i = 0 var i = 0
val n = map.keys_.size val n = map.keys_.size
while (i < n) { while (i < n) {
val key = keys[i] val key = keys[i]
put(key, map.get(key)) put(key, map.get(key)!!) // we know this is value, because we checked it earlier
i++ i++
} }
} }
@ -169,7 +169,10 @@ class OrderedMap<K, V> : ObjectMap<K, V?> where K : Any, K : Comparable<K> {
if (containsKey(after)) return false if (containsKey(after)) return false
val index = keys_.indexOf(before) val index = keys_.indexOf(before)
if (index == -1) return false if (index == -1) return false
super.put(after, super.remove(before)) val prev = super.remove(before)
if (prev != null) {
super.put(after, prev)
}
keys_[index] = after keys_[index] = after
return true return true
} }
@ -186,7 +189,10 @@ class OrderedMap<K, V> : ObjectMap<K, V?> where K : Any, K : Comparable<K> {
*/ */
fun alterIndex(index: Int, after: K): Boolean { fun alterIndex(index: Int, after: K): Boolean {
if (index < 0 || index >= size || containsKey(after)) return false if (index < 0 || index >= size || containsKey(after)) return false
super.put(after, super.remove(keys_[index])) val prev = super.remove(keys_[index])
if (prev != null) {
super.put(after, prev)
}
keys_[index] = after keys_[index] = after
return true return true
} }

View File

@ -182,7 +182,7 @@ class IntTests {
assertTrue(map.size == 3) assertTrue(map.size == 3)
val entries = map.entries() val entries = map.entries()
val keepEntry = IntMap.Entry<String?>() val keepEntry = IntMap.Entry<String?>(map)
keepEntry.key = 1 keepEntry.key = 1
keepEntry.value = "1" keepEntry.value = "1"
@ -326,7 +326,7 @@ class IntTests {
assertTrue(map.size == 3) assertTrue(map.size == 3)
val entries = map.entries() val entries = map.entries()
val keepEntry = IntIntMap.Entry() val keepEntry = IntIntMap.Entry(map)
keepEntry.key = 1 keepEntry.key = 1
keepEntry.value = 1 keepEntry.value = 1
@ -372,7 +372,7 @@ class IntTests {
val map = map3() val map = map3()
assertTrue(map.size == 3) assertTrue(map.size == 3)
val keys = map.keys() val keys = map.keys
assertTrue(keys.size == 3) assertTrue(keys.size == 3)
assertTrue(map[2] == "2") assertTrue(map[2] == "2")
@ -400,7 +400,7 @@ class IntTests {
val map = map3() val map = map3()
assertTrue(map.size == 3) assertTrue(map.size == 3)
val values = map.values() val values = map.values
assertTrue(values.size == 3) assertTrue(values.size == 3)
values.remove("2") values.remove("2")
@ -427,10 +427,10 @@ class IntTests {
val map = map3() val map = map3()
assertTrue(map.size == 3) assertTrue(map.size == 3)
val entries = map.entries() val entries = map.entries
assertTrue(entries.size == 3) assertTrue(entries.size == 3)
var toRemove: IntMap.Entry<String?>? = null var toRemove: MutableMap.MutableEntry<Int, String?>? = null
val iter = entries.iterator() val iter = entries.iterator()
while (iter.hasNext()) { while (iter.hasNext()) {
val entry = iter.next() val entry = iter.next()
@ -463,29 +463,6 @@ class IntTests {
assertTrue(entries.size == 0) assertTrue(entries.size == 0)
} }
@Test
fun testLFIntMapEntries2() {
val map = map3()
assertTrue(map.size == 3)
val entries = map.entries()
val keepEntry = IntMap.Entry<String?>()
keepEntry.key = 1
keepEntry.value = "1"
val keep = listOf(keepEntry)
entries.retainAll(keep)
assertTrue(map.size == 1)
assertTrue(map[1] == "1")
entries.clear()
assertTrue(map.isEmpty())
assertTrue(entries.isEmpty())
assertTrue(map.size == 0)
assertTrue(entries.size == 0)
}
@Test @Test
fun testIntSet() { fun testIntSet() {
val set = set() val set = set()

View File

@ -163,7 +163,8 @@ class ObjectTests {
assertTrue(map.size == 3) assertTrue(map.size == 3)
val entries = map.entries() val entries = map.entries()
val keepEntry = ObjectMap.Entry<String, Int?>(map) entries.findNextIndex()
val keepEntry = ObjectMap.Entry<String, Int?>(map, entries.nextIndex)
keepEntry.key = "1" keepEntry.key = "1"
keepEntry.value = 1 keepEntry.value = 1