Code polish

master
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
* 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
var i = size_ - 1
if (key == null) {
@ -246,6 +246,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
i--
}
}
return defaultValue
}
@ -626,7 +627,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
val key = keys[i] as K
val value = values[i]
if (value == null) {
if (other.get(key, dummy as V?) != null) {
if (other.get(key, dummy as V) != null) {
return false
}
}
@ -656,7 +657,7 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
var i = 0
val n = size_
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++
}
return true
@ -787,11 +788,17 @@ class ArrayMap<K: Any, V> : MutableMap<K, V?>{
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)
var index = 0
var valid = true
private lateinit var entry: Entry<K, V?>
internal var index = 0
internal var valid = true
init {
if (hasNext()) {
entry = Entry(map)
}
}
override fun hasNext(): Boolean {
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 (!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++]
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?> {
override lateinit var key: K
override var value: V? = null
class Entry<K: Any, V>(val map: ArrayMap<K, V>) : MutableMap.MutableEntry<K, V?> {
// we know there will be at least one
override var key: K = map.keyTable[0]!!
override var value: V? = map.valueTable[0]
override fun setValue(newValue: V?): V? {
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?>
var index = 0
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>
var index = 0
var valid = true

View File

@ -238,8 +238,8 @@ open class IntMap<V> : MutableMap<Int, V> {
return if (i >= 0) valueTable[i] else null
}
operator fun get(key: Int, defaultValue: V?): V? {
if (key == 0) return if (hasZeroValue) zeroValue else defaultValue
operator fun get(key: Int, defaultValue: V): V? {
if (key == 0) return if (hasZeroValue) zeroValue!! else defaultValue
val i = locateKey(key)
return if (i >= 0) valueTable[i] else defaultValue
}
@ -489,7 +489,7 @@ open class IntMap<V> : MutableMap<Int, V> {
if (key != 0) {
val value: V? = valueTable[i]
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 {
if (value != other[key]) return false
@ -518,7 +518,7 @@ open class IntMap<V> : MutableMap<Int, V> {
val n = keyTable.size
while (i < n) {
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++
}
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
*
* @return the previous value associated with <tt>key</tt>, or
* <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>.)
* @return the previous value associated with [key]
*/
@Synchronized
override fun remove(key: K): V? {
override fun remove(key: K): V {
val value = forwardHashMap.remove(key)
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
*/
class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
class LockFreeHashMap<K: Any, V> : MutableMap<K, V>, Cloneable, Serializable {
@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
@ -96,11 +96,11 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
hashMap = HashMap(initialCapacity, loadFactor)
}
val map: MutableMap<K, V>
get() {
@Suppress("UNCHECKED_CAST")
return mapREF[this] as MutableMap<K, V>
}
private val map: MutableMap<K, V>
get() {
@Suppress("UNCHECKED_CAST")
return mapREF[this] as MutableMap<K, V>
}
override val size: Int
@ -114,16 +114,14 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
return map.keys
}
override val values: MutableCollection<V?>
override val values: MutableCollection<V>
get() {
@Suppress("UNCHECKED_CAST")
return map.values as MutableCollection<V?>
return map.values
}
override val entries: MutableSet<MutableMap.MutableEntry<K, V?>>
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() {
@Suppress("UNCHECKED_CAST")
return map.entries as MutableSet<MutableMap.MutableEntry<K, V?>>
return map.entries
}
override fun isEmpty(): Boolean {
@ -136,7 +134,7 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
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
return mapREF[this].containsValue(value)
}
@ -147,7 +145,7 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
}
@Synchronized
override fun put(key: K, value: V?): V? {
override fun put(key: K, value: V): V? {
return hashMap.put(key, value)
}
@ -168,7 +166,7 @@ class LockFreeHashMap<K: Any, V> : MutableMap<K, V?>, Cloneable, Serializable {
}
@Synchronized
override fun putAll(from: Map<out K, V?>) {
override fun putAll(from: Map<out K, V>) {
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
*
* @return the previous value associated with <tt>key</tt>, or
* <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>.)
* @return the previous value associated with [key]
*/
@Synchronized
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
* the capacity. [OrderedMap] provides much faster iteration.
*/
class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
class LockFreeIntMap<V> : MutableMap<Int, V>, Cloneable, Serializable {
@Volatile
private var hashMap: IntMap<V>
@ -107,7 +107,11 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
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
return mapREF[this].containsValue(value, identity)
}
@ -130,7 +134,7 @@ class LockFreeIntMap<V> : IntMap<V>, Cloneable, Serializable {
}
@Synchronized
override fun putAll(from: IntMap<out V>) {
override fun putAll(from: Map<out Int, V>) {
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
* time this method is called. Use the [ObjectMap.Entries] constructor for nested or multithreaded iteration.
*/
override fun keys(): Keys {
return mapREF[this].keys()
}
override val keys: IntMap.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!
@ -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.
*/
@Suppress("UNCHECKED_CAST")
override fun values(): Values<V> {
return mapREF[this].values() as Values<V>
}
override val values: IntMap.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!
@ -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.
*/
@Suppress("UNCHECKED_CAST")
override fun entries(): Entries<V?> {
return mapREF[this].entries() as Entries<V?>
}
override val entries: MutableSet<MutableMap.MutableEntry<Int, V>>
get() {
return mapREF[this].entries() as MutableSet<MutableMap.MutableEntry<Int, V>>
}
override fun equals(other: Any?): Boolean {
return mapREF[this] == other
}
override fun equalsIdentity(other: Any?): Boolean {
fun equalsIdentity(other: Any?): Boolean {
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.
*/
@Synchronized
override fun clear(maximumCapacity: Int) {
fun clear(maximumCapacity: Int) {
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.
*/
@Synchronized
override fun shrink(maximumCapacity: Int) {
fun shrink(maximumCapacity: Int) {
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 {
@Volatile
private var forwardHashMap: MutableMap<K, V>
private var forwardHashMap: ObjectMap<K, V>
@Volatile
private var reverseHashMap: MutableMap<V, K>
private var reverseHashMap: ObjectMap<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)
}
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.reverseHashMap = reverseHashMap
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
private val forwardREF = AtomicReferenceFieldUpdater.newUpdater(
LockFreeObjectBiMap::class.java, MutableMap::class.java, "forwardHashMap"
LockFreeObjectBiMap::class.java, ObjectMap::class.java, "forwardHashMap"
)
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
*
* @return the previous value associated with <tt>key</tt>, or
* <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>.)
* @return the previous value associated with [key] or [defaultReturnValue] if it doesn't exist
*
*/
@Synchronized
override fun remove(key: K): Int? {
override fun remove(key: K): Int {
val value = forwardHashMap.remove(key)
reverseHashMap.remove(value)
return value
return value ?: defaultReturnValue
}
/**
* 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
@ -309,9 +307,9 @@ class LockFreeObjectIntBiMap<K: Any> : MutableMap<K, Int>, Cloneable, Serializab
* 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
* 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
* distinguish these two cases.
*
@ -319,8 +317,7 @@ class LockFreeObjectIntBiMap<K: Any> : MutableMap<K, Int>, Cloneable, Serializab
*/
override operator fun get(key: K): Int {
// use the SWP to get a lock-free get of the value
@Suppress("UNCHECKED_CAST")
return (forwardREF[this] as ObjectIntMap<K>)[key] as Int
return forwardREF[this][key] ?: defaultReturnValue
}
/**

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
* 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
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
// 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)
}
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
return mapREF[this].containsValue(value, identity)
}
@ -120,7 +125,7 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
}
@Synchronized
override fun put(key: K, value: V?): V? {
override fun put(key: K, value: V): V? {
return hashMap.put(key, value)
}
@ -130,7 +135,7 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
}
@Synchronized
override fun putAll(from: ObjectMap<out K, out V?>) {
override fun putAll(from: Map<out K, V>) {
hashMap.putAll(from)
}
@ -139,6 +144,8 @@ class LockFreeObjectMap<K: Any, V> : ObjectMap<K, V?>, Cloneable, Serializable {
hashMap.clear()
}
/**
* 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.
*/
@Suppress("UNCHECKED_CAST")
override fun keys(): Keys<K> {
return mapREF[this].keys() as Keys<K>
}
override val keys: ObjectMap.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!
@ -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.
*/
@Suppress("UNCHECKED_CAST")
override fun values(): Values<V?> {
return mapREF[this].values() as Values<V?>
}
override val values: ObjectMap.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!
@ -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.
*/
@Suppress("UNCHECKED_CAST")
override fun entries(): Entries<K, V?> {
return mapREF[this].entries() as Entries<K, V?>
}
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() {
return mapREF[this].entries() as MutableSet<MutableMap.MutableEntry<K, V>>
}
override fun equals(other: Any?): Boolean {
return mapREF[this] == other
}
override fun equalsIdentity(other: Any?): Boolean {
fun equalsIdentity(other: Any?): Boolean {
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.
*/
@Synchronized
override fun clear(maximumCapacity: Int) {
fun clear(maximumCapacity: Int) {
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.
*/
@Synchronized
override fun shrink(maximumCapacity: Int) {
fun shrink(maximumCapacity: Int) {
mapREF[this].shrink(maximumCapacity)
}

View File

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

View File

@ -59,7 +59,7 @@ import java.util.*
* @author Nathan Sweet
* @author Tommy Ettinger
*/
open class ObjectMap<K: Any, V> : MutableMap<K, V> {
open class ObjectMap<K: Any, V> : MutableMap<K, V?> {
companion object {
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.
*/
override fun put(key: K, value: V): V? {
override fun put(key: K, value: V?): V? {
var i = locateKey(key)
if (i >= 0) { // Existing key was found.
val oldValue = valueTable[i]
@ -203,7 +203,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
return null
}
open fun putAll(from: ObjectMap<out K, out V>) {
open fun putAll(from: ObjectMap<out K, out V?>) {
ensureCapacity(from.mapSize)
val keyTable = from.keyTable
@ -214,13 +214,13 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
while (i < n) {
key = keyTable[i]
if (key != null) {
put(key, valueTable[i]!!)
put(key, valueTable[i])
}
i++
}
}
override fun putAll(from: Map<out K, V>) {
override fun putAll(from: Map<out K, V?>) {
ensureCapacity(from.size)
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.
*/
operator fun get(key: K, defaultValue: V?): V? {
operator fun get(key: K, defaultValue: V): V? {
val i = locateKey(key)
return if (i < 0) {
defaultValue
@ -338,7 +338,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
Arrays.fill(valueTable, null)
}
override fun containsValue(value: V): Boolean {
override fun containsValue(value: V?): Boolean {
return containsValue(value, false)
}
@ -452,7 +452,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
if (key != null) {
val value = valueTable[i]
if (value == null) {
if (other.get(key, dummy as V?) != null) return false
if (other.get(key, dummy as V) != null) return false
}
else {
if (value != other.get(key)) return false
@ -478,7 +478,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
val n = keyTable.size
while (i < n) {
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++
}
return true
@ -523,8 +523,8 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
return buffer.toString()
}
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
get() = entries() as MutableSet<MutableMap.MutableEntry<K, V>>
override val entries: 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?>
}
override val values: MutableCollection<V>
override val values: MutableCollection<V?>
get() = values()
/**
@ -563,7 +563,7 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
*
* 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 (values1 == null) {
values1 = Values(this as ObjectMap<K, V?>)
@ -573,12 +573,12 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
values1!!.reset()
values1!!.valid = true
values2!!.valid = false
return values1 as Values<V>
return values1 as Values<V?>
}
values2!!.reset()
values2!!.valid = true
values1!!.valid = false
return values2 as Values<V>
return values2 as Values<V?>
}
override val keys: MutableSet<K>
@ -601,17 +601,18 @@ open class ObjectMap<K: Any, V> : MutableMap<K, V> {
keys1!!.reset()
keys1!!.valid = true
keys2!!.valid = false
return keys1 as Keys<K>
return keys1!!
}
keys2!!.reset()
keys2!!.valid = true
keys1!!.valid = false
return keys2 as Keys<K>
return keys2!!
}
class Entry<K: Any, V>(val map: ObjectMap<K, V?>) : MutableMap.MutableEntry<K, V?> {
override lateinit var key: K
override var value: V? = null
class Entry<K: Any, V>(val map: ObjectMap<K, V?>, index: Int) : MutableMap.MutableEntry<K, V?> {
// we know there will be at least one
override var key: K = map.keyTable[index]!!
override var value: V? = map.valueTable[index]
override fun setValue(newValue: V?): V? {
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 nextIndex = 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) {
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. */
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 {
if (!valid) throw RuntimeException("#iterator() cannot be used nested.")
return hasNext

View File

@ -63,7 +63,7 @@ import dorkbox.collections.Collections.allocateIterators
* @author Nathan Sweet
* @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 {
const val version = Collections.version
}
@ -132,14 +132,14 @@ class OrderedMap<K, V> : ObjectMap<K, V?> where K : Any, K : Comparable<K> {
return null
}
fun putAll(map: OrderedMap<K, out V?>) {
fun putAll(map: OrderedMap<K, V>) {
ensureCapacity(map.size)
val keys = map.keys_
var i = 0
val n = map.keys_.size
while (i < n) {
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++
}
}
@ -169,7 +169,10 @@ class OrderedMap<K, V> : ObjectMap<K, V?> where K : Any, K : Comparable<K> {
if (containsKey(after)) return false
val index = keys_.indexOf(before)
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
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 {
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
return true
}

View File

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

View File

@ -163,7 +163,8 @@ class ObjectTests {
assertTrue(map.size == 3)
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.value = 1