/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.
/**
* @file
*
* This file defines HashMap and related classes.
*/
package std.collection
struct HashMapEntry<K, V> {
public let hash: Int64
public let next: Int64
public let key: K
public let value: V
@Frozen
init(h: Int64, n: Int64, k: K, v: V) {
hash = h
next = n
key = k
value = v
}
@Frozen
init() {
hash = -1
next = -1
key = unsafe { zeroValue<K>() }
value = unsafe { zeroValue<V>() }
}
}
/**
* This class is a collection of K types of hashmap and is suitable for stripping key-value pair types.
*
* @since 0.24.2
*/
class HashMapKeys<K, V> <: EquatableCollection<K> where K <: Hashable & Equatable<K> {
private let map: HashMap<K, V>
@Frozen
init(m: HashMap<K, V>) {
map = m
}
/**
* Returns sizes of key-value.
*
* @return sizes of key-value.
*
* @since 0.24.2
*/
@Frozen
public prop size: Int64 {
get() {
return map.size
}
}
/**
* Check whether the size is empty. If yes, true is returned. Otherwise, false is returned.
*
* @return bool if yes, true is returned. Otherwise, false is returned.
*
* @since 0.24.2
*/
@Frozen
public func isEmpty(): Bool {
return map.isEmpty()
}
/**
* Checks whether the mapping relationship corresponding to the specified key exists in this mapping.
*
* @param element transfer the key to be judged.
* @return bool returns true if exists; otherwise, false.
*
* @since 0.24.2
*/
@Frozen
public func contains(element: K): Bool {
return map.contains(element)
}
/**
* Checks whether the mapping relationship corresponding to the collection key exists in this mapping.
*
* @param elements transfer the collection key to be judged.
* @return bool returns true if exists; otherwise, false.
*
* @since 0.24.2
*/
@Frozen
public func contains(all!: Collection<K>): Bool {
return map.contains(all: all)
}
/**
* Returns iterator of hashmap.
*
* @return iterator of hashmap.
*
* @since 0.24.2
*/
@Frozen
public func iterator(): Iterator<K> {
return map.iterator().map<K> {i: (K, V) => i[0]}
}
/**
* Returns the keys in this Map as an Array.
*/
@Frozen
public func toArray(): Array<K> {
let keys = Array<K>(size, repeat: unsafe { zeroValue<K>() })
var index = 0
var pos = 0
while (index < size) {
if (map.entries[pos].hash >= 0) {
keys[index] = map.entries[pos].key
index++
}
pos++
}
return keys
}
}
/**
* This class is a collection of V types of hashmaps and is suitable for stripping key-value pair types.
*
* @since 0.24.2
*/
class HashMapValues<K, V> <: Collection<V> where K <: Hashable & Equatable<K> {
private let map: HashMap<K, V>
@Frozen
init(m: HashMap<K, V>) {
map = m
}
/**
* Returns sizes of values.
*
* @return sizes of values.
*
* @since 0.24.2
*/
@Frozen
public prop size: Int64 {
get() {
return map.size
}
}
/**
* Check whether the size is empty. If yes, true is returned. Otherwise, false is returned.
*
* @return bool if yes, true is returned. Otherwise, false is returned.
*
* @since 0.24.2
*/
@Frozen
public func isEmpty(): Bool {
return map.isEmpty()
}
/**
* Returns value Iterator<T> - Iterator<T> type, which can be traversed using an iterator.
*
* @return the value corresponding to the return key is encapsulated with option.
*
* @since 0.24.2
*/
@Frozen
public func iterator(): Iterator<V> {
return map.iterator().map<V> {i: (K, V) => i[1]}
}
/**
* Returns the values in this Map as an Array.
*/
@Frozen
public func toArray(): Array<V> {
let values = Array<V>(size, repeat: unsafe { zeroValue<V>() })
var index = 0
var pos = 0
while (index < size) {
if (map.entries[pos].hash >= 0) {
values[index] = map.entries[pos].value
index++
}
pos++
}
return values
}
}
/**
* Class HashMapEntryView is a reference view of a key in a hashmap.
* Thread safety is not guaranteed.
*/
class HashMapEntryView<K, V> <: MapEntryView<K, V> where K <: Hashable & Equatable<K> {
// Reference to a HashMap.
private let map: HashMap<K, V>
// The specified key.
private let _key: K
// Hash of a specified key.
private let hash: Int64
// Index of a specified key.
private var index: Int64
private var _isAbsent: Bool
// The HashMap's version.
private var lockVersion: Int64
@Frozen
init(isAbsent: Bool, key: K, hash: Int64, index: Int64, map: HashMap<K, V>) {
this._isAbsent = isAbsent
this._key = key
this.hash = hash
this.map = map
this.index = index
this.lockVersion = map.version()
}
@Frozen
public prop key: K {
get() {
getKey()
}
}
@Frozen
public mut prop value: ?V {
get() {
getValue()
}
set(value) {
return if (value.isNone()) {
map.remove(_key)
_isAbsent = true
} else {
setValue(value.getOrThrow())
}
}
}
/**
* Get the key of HashMapEntryView with time complexity O(1).
*
* Returns key.
*
* @since 0.45.1
*/
@Frozen
func getKey(): K {
if (lockVersion != map.version()) {
throw ConcurrentModificationException()
}
return _key
}
/**
* Get the value of HashMapEntryView with time complexity O(1).
*
* Returns value.
*
* @since 0.45.1
*/
@Frozen
func getValue(): ?V {
if (lockVersion != map.version()) {
throw ConcurrentModificationException()
}
return if (_isAbsent) {
None
} else {
map.entries[index].value
}
}
/**
* Update the value of HashMapEntryView with time complexity O(1).
*
* If the view is empty, the key-value pair is inserted, and the view is converted into a non-empty view and the value is returned.
* Otherwise, the value is modified.
*
* @since 0.45.1
*/
@Frozen
private func setValue(v: V) {
if (lockVersion != map.version()) {
throw ConcurrentModificationException()
}
if (_isAbsent) {
var bucketIndex: Int64 = map.getIndex(hash, map.buckets.size)
if (map.freeSize > 0) {
index = map.freeIndex
map.freeIndex = map.entries[index].next
map.freeSize--
} else {
if (map.resize(map.size + 1)) {
bucketIndex = map.getIndex(hash, map.buckets.size)
}
index = map.appendIndex
map.appendIndex++
}
map.entries[index] = HashMapEntry<K, V>(hash, map.buckets[bucketIndex], key, v)
map.modCount++
map.buckets[bucketIndex] = index
lockVersion = map.modCount
_isAbsent = false
} else {
let temp = map.entries[index]
map.entries[index] = HashMapEntry<K, V>(temp.hash, temp.next, temp.key, v)
}
}
}
/**
* The hashmap is implemented based on the hash algorithm.
* You can use put key-value to obtain the value and get key to obtain the value.
*
* @since 0.18.4
*/
public class HashMap<K, V> <: Map<K, V> where K <: Hashable & Equatable<K> {
/* Max size of buckets. */
private static const MAX_BUCKET_SIZE: Int64 = 4611686018427387904
/*
* Load factor of the HashMap.
* Capacity expansion when the usage more than 75%
*/
private static const LOAD_FACTOR: Float32 = 0.75
/*
* default Initialized Capacity.
* Default array initialization size is 16
*/
private static const DEFAULT_CAPACITY: Int64 = 16
/* Maximum number of int64 types. */
/* Subscripts that can be directly inserted*/
var appendIndex: Int64 = 0
/* Subscript of the currently available deleted entry.*/
var freeIndex: Int64 = -1
/* Total number of deleted entries that are available.*/
var freeSize: Int64 = 0
/* Modified hashmap version.*/
var modCount: Int64 = 0b00000000
/* Index Array.*/
var buckets: Array<Int64>
/* Element Array.*/
var entries: Array<HashMapEntry<K, V>>
/**
* Initializes an empty HashMap with a default initial DEFAULT_CAPACITY (16) and a default load factor.
*
* @since 0.18.4
*/
@Frozen
public init() {
buckets = Array<Int64>(DEFAULT_CAPACITY, repeat: -1)
entries = Array<HashMapEntry<K, V>>(
DEFAULT_CAPACITY,
repeat: HashMapEntry<K, V>()
)
}
/**
* Initializes a HashMap with an incoming iterator for initialization.
*
* @param elements an incoming iterator is initialized.
*
* @since 0.18.4
*/
@Frozen
public init(elements: Collection<(K, V)>) {
let bucketSize: Int64 = elements.size + (elements.size >> 1)
buckets = Array<Int64>(tableSizeFor(bucketSize), repeat: -1)
entries = Array<HashMapEntry<K, V>>(elements.size, repeat: HashMapEntry<K, V>())
for (i in elements) {
putWithoutResize(i[0], i[1])
}
}
/**
* Initializes a hashmap with an incoming list for initialization.
*
* @param elements an incoming list is initialized.
*
* @since 0.18.4
*/
@Frozen
public init(elements: Array<(K, V)>) {
let bucketSize: Int64 = elements.size + (elements.size >> 1)
buckets = Array<Int64>(tableSizeFor(bucketSize), repeat: -1)
entries = Array<HashMapEntry<K, V>>(elements.size, repeat: HashMapEntry<K, V>())
for (i in elements) {
putWithoutResize(i[0], i[1])
}
}
/**
* Initializes a HashMap with an incoming @p DEFAULT_CAPACITY for initialization.
*
* @param capacity an incoming capacity is initialized.
*
* @throws IllegalArgumentException if capacity is less than zero
*
* @since 0.18.4
*/
@Frozen
public init(capacity: Int64) {
if (capacity < 0) {
throw IllegalArgumentException("Invalid size of HashMap: ${capacity}.")
}
let bucketSize = capacity + (capacity >> 1)
buckets = Array<Int64>(tableSizeFor(bucketSize), repeat: -1)
entries = Array<HashMapEntry<K, V>>(capacity, repeat: HashMapEntry<K, V>())
}
/**
* Initializes a hash map with an incoming size and an initial element for initialization.
*
* @param size the size of the incoming initial element.
* @param initElement an incoming initElement is initialized.
*
* @throws IllegalArgumentException if size is less than zero
*
* @since 0.18.4
*/
@Frozen
public init(size: Int64, initElement: (Int64) -> (K, V)) {
if (size < 0) {
throw IllegalArgumentException("Invalid size of HashMap: ${size}.")
} else {
buckets = Array<Int64>(tableSizeFor(size), repeat: -1)
entries = Array<HashMapEntry<K, V>>(size, repeat: HashMapEntry<K, V>())
for (i in 0..size) {
let element: (K, V) = initElement(i)
add(element[0], element[1])
}
}
}
@Frozen
private init(other: HashMap<K, V>) {
buckets = other.buckets.clone()
entries = other.entries.clone()
appendIndex = other.appendIndex
freeIndex = other.freeIndex
freeSize = other.freeSize
}
/**
* Returns value Iterator<T> - Iterator<T> type, which can be traversed using an iterator.
*
* @param key transfer key to obtain the value.
* @return the value corresponding to the return key is encapsulated with option.
*
* @since 0.18.4
*/
@Frozen
public func get(key: K): ?V {
let hash: Int64 = getHash(key.hashCode())
let index: Int64 = getIndex(hash, buckets.size)
var bucket: Int64 = buckets[index]
while (bucket >= 0) {
if (hash == entries[bucket].hash && key == entries[bucket].key) {
return entries[bucket].value
}
bucket = entries[bucket].next
}
return None
}
/**
* Associates the specified @p value with the specified @p key in this map.
* If you map a mapping that previously contained a key, the old value is replaced.
*
* @param key the key to put.
* @param value the value to assign.
* @return If the key exists before the assignment, the value before the assignment is encapsulated with Option.
* Otherwise, return to Option<V>.None
*
* @since 0.18.4
*/
@Frozen
@OverflowWrapping
public func add(key: K, value: V): Option<V> {
let hash: Int64 = getHash(key.hashCode())
var bucketIndex: Int64 = getIndex(hash, buckets.size)
var entryIndex: Int64 = buckets[bucketIndex]
while (entryIndex >= 0) {
if (hash == entries[entryIndex].hash && key == entries[entryIndex].key) {
let oldValue: Option<V> = Some(entries[entryIndex].value)
entries[entryIndex] = HashMapEntry<K, V>(entries[entryIndex].hash, entries[entryIndex].next,
entries[entryIndex].key, value)
return oldValue
}
entryIndex = entries[entryIndex].next
}
if (freeSize > 0) {
entryIndex = freeIndex
freeIndex = entries[entryIndex].next
freeSize--
} else {
if (resize(size + 1)) {
bucketIndex = getIndex(hash, buckets.size)
}
entryIndex = appendIndex
appendIndex++
}
entries[entryIndex] = HashMapEntry<K, V>(hash, buckets[bucketIndex], key, value)
modCount++
buckets[bucketIndex] = entryIndex
return Option<V>.None
}
@Frozen
@OverflowWrapping
protected func putWithoutResize(key: K, value: V): Unit {
let hash: Int64 = getHash(key.hashCode())
var bucketIndex: Int64 = getIndex(hash, buckets.size)
var entryIndex: Int64 = buckets[bucketIndex]
while (entryIndex >= 0) {
if (hash == entries[entryIndex].hash && key == entries[entryIndex].key) {
entries[entryIndex] = HashMapEntry<K, V>(entries[entryIndex].hash, entries[entryIndex].next,
entries[entryIndex].key, value)
return
}
entryIndex = entries[entryIndex].next
}
entryIndex = appendIndex
appendIndex++
entries[entryIndex] = HashMapEntry<K, V>(hash, buckets[bucketIndex], key, value)
modCount++
buckets[bucketIndex] = entryIndex
}
/**
* A view of a single entry in a hashmap, which can be empty or has a value.
*
* @param key the key to put.
* @return If the hashmap has this key, a view with a value is returned. Otherwise, an empty view is returned.
*
* @since 0.45.1
*/
@Frozen
public func entryView(key: K): MapEntryView<K, V> {
let hash: Int64 = getHash(key.hashCode())
var index: Int64 = getIndex(hash, buckets.size)
var entryIndex: Int64 = buckets[index]
while (entryIndex >= 0) {
if (hash == entries[entryIndex].hash && key == entries[entryIndex].key) {
return HashMapEntryView<K, V>(false, key, hash, entryIndex, this)
}
entryIndex = entries[entryIndex].next
}
return HashMapEntryView<K, V>(true, key, hash, entryIndex, this)
}
/**
* Transfer specified elements for traversal and assign values in sequence.
* If you map a mapping that previously contained a key, the old value is replaced.
*
* @param elements the element passing in for traversal assignment.
*
* @since 0.18.4
*/
@Frozen
public func add(all!: Collection<(K, V)>): Unit {
if (all.size > entries.size) {
resize(all.size)
}
for ((k, v) in all) {
add(k, v)
}
}
/**
* Removes the key-value pair corresponding to the key based on the specified key from this mapping, if one exists.
*
* @param key pass in the key to be deleted.
* @return removed element
*
* @since 0.18.4
*/
@Frozen
@OverflowWrapping
public func remove(key: K): Option<V> {
let NULL_KEY: K = unsafe { zeroValue<K>() }
let NULL_VALUE: V = unsafe { zeroValue<V>() }
let hash: Int64 = getHash(key.hashCode())
let index: Int64 = getIndex(hash, buckets.size)
var bucket: Int64 = buckets[index]
var pre: Int64 = -1
while (bucket >= 0) {
if (hash == entries[bucket].hash && key == entries[bucket].key) {
if (pre < 0) {
buckets[index] = entries[bucket].next
} else {
let temp = entries[pre]
entries[pre] = HashMapEntry<K, V>(temp.hash, entries[bucket].next, temp.key, temp.value)
}
let removeEnt: V = entries[bucket].value
entries[bucket] = HashMapEntry<K, V>(-1, freeIndex, NULL_KEY, NULL_VALUE)
freeIndex = bucket
freeSize++
modCount++
return Some(removeEnt)
}
pre = bucket
bucket = entries[bucket].next
}
return None
}
/**
* Traverse the set of transferred keys and delete them based on the traversal result.
*
* @param keys pass in the collection to traverse.
*
* @since 0.18.4
*/
@Frozen
public func remove(all!: Collection<K>): Unit {
for (key in all) {
remove(key)
}
}
/**
* Transfer a lambda expression and delete the corresponding key value if the condition is met.
*
* @param predicate transfer a lambda expression for judgment.
*
* @since 0.18.4
*/
@Frozen
@OverflowWrapping
public func removeIf(predicate: (K, V) -> Bool): Unit {
let NULL_KEY: K = unsafe { zeroValue<K>() }
let NULL_VALUE: V = unsafe { zeroValue<V>() }
var lockVersion = modCount
for (i in 0..buckets.size where buckets[i] != -1) {
var next: Int64 = buckets[i]
var pre: Int64 = -1
while (next >= 0) {
let item = entries[next]
if (predicate(item.key, item.value)) {
if (lockVersion != modCount) {
throw ConcurrentModificationException("The predicate cannot contain a modify operation.")
}
if (pre < 0) {
buckets[i] = item.next
} else {
let temp = entries[pre]
entries[pre] = HashMapEntry<K, V>(temp.hash, item.next, temp.key, temp.value)
}
entries[next] = HashMapEntry<K, V>(-1, freeIndex, NULL_KEY, NULL_VALUE)
freeIndex = next
freeSize++
modCount++
lockVersion++
} else {
if (lockVersion != modCount) {
throw ConcurrentModificationException("The predicate cannot contain a modify operation.")
}
pre = next
}
next = item.next
}
}
}
/**
* Clear all key-value pairs.
*
* @since 0.18.4
*/
@Frozen
@OverflowWrapping
public func clear(): Unit {
for (i in 0..entries.size where entries[i].hash >= 0) {
entries[i] = HashMapEntry<K, V>()
}
for (i in 0..buckets.size where buckets[i] != -1) {
buckets[i] = -1
}
modCount++
freeIndex = -1
appendIndex = 0
freeSize = 0
}
/**
* Reserves capacity for at least additional more elements to be inserted in this hashmap.
* If the additional parameter is negative, the hash map does not change.
* If the additional parameter plus the size of the hashmap is smaller than the capacity of the array, the hashmap does not change.
*
* @param additional ensure that there is sufficient capacity. additional indicates the quantity to be added.
*
* @since 0.18.4
*/
@Frozen
public func reserve(additional: Int64): Unit {
if (additional <= 0 || size + additional < capacity) {
return
}
resize(size + additional)
modCount++
}
/**
* Returns the capacity of the hashmap.
* The return value is the size of the array, not the size of the elements that can be accommodated.
*
* @return the capacity of the hashmap.
*
* @since 0.18.4
*/
@Frozen
public prop capacity: Int64 {
get() {
return entries.size
}
}
/**
* Checks whether the mapping relationship corresponding to the collection key exists in this mapping.
*
* @param keys transfer the collection key to be judged.
* @return bool returns true if exists; otherwise, false.
*
* @since 0.18.4
*/
@Frozen
public func contains(all!: Collection<K>): Bool {
for (key in all where !contains(key)) {
return false
}
return true
}
/**
* Checks whether the mapping relationship corresponding to the specified key exists in this mapping.
*
* @param key transfer the key to be judged.
* @return bool returns true if exists; otherwise, false.
*
* @since 0.18.4
*/
@Frozen
public func contains(key: K): Bool {
if (appendIndex - freeSize == 0) {
return false
}
let hash: Int64 = getHash(key.hashCode())
let index: Int64 = getIndex(hash, buckets.size)
var bucket: Int64 = buckets[index]
while (bucket >= 0) {
if (hash == entries[bucket].hash && key == entries[bucket].key) {
return true
}
bucket = entries[bucket].next
}
return false
}
/**
* Copy a HashMap.
*
* @return a clone value of HashMap.
*
* @since 0.18.4
*/
@Frozen
public func clone(): HashMap<K, V> {
return HashMap<K, V>(this)
}
/**
* Returns the Set view of all keys in this HashMap.
*
* @return the Set view of the keys.
*
* @since 0.18.4
*/
@Frozen
public func keys(): EquatableCollection<K> {
return HashMapKeys<K, V>(this)
}
/**
* Returns the Set view of all values in this HashMap.
*
* @return the list view of the values.
*
* @since 0.18.4
*/
@Frozen
public func values(): Collection<V> {
return HashMapValues<K, V>(this)
}
/**
* @description Performs mapping on this HashMap and returns a new HashMap.
*
* @param transform the given mapping function.
* @returns a new HashMap.
*/
@When[env != "ohos"]
public func mapValues<R>(transform: (K, V) -> R): HashMap<K, R> {
let size: Int64 = entries.size
var newMap = HashMap<K, R>()
for (i in 0..size) {
if (entries[i].hash >= 0) {
newMap.add(entries[i].key, transform(entries[i].key, entries[i].value))
}
}
return newMap
}
/**
* @description Performs mapping on this HashMap and returns a new HashMap.
*
* @param transform the given mapping function.
* @returns a new HashMap.
*/
@When[env != "ohos"]
public func mapValues<R>(transform: (V) -> R): HashMap<K, R> {
let size: Int64 = entries.size
var newMap = HashMap<K, R>()
for (i in 0..size) {
if (entries[i].hash >= 0) {
newMap.add(entries[i].key, transform(entries[i].value))
}
}
return newMap
}
/**
* @description Returns a new HashMap<K, V> containing key-value pairs that satisfy the filtering condition.
*
* @param predicate the given condition.
* @returns a new collection of key-value pairs that satisfy the filtering condition.
*/
@When[env != "ohos"]
public func filter(predicate: (K, V) -> Bool): HashMap<K, V> {
let size: Int64 = entries.size
var newMap = HashMap<K, V>()
for (i in 0..size) {
if (entries[i].hash >= 0 && predicate(entries[i].key, entries[i].value)) {
newMap.add(entries[i].key, entries[i].value)
}
}
return newMap
}
/**
* @description Iterates over all key-value pairs and performs the given operation.
*
* @param action the given operation function.
*/
@When[env != "ohos"]
public func forEach(action: (K, V) -> Unit): Unit {
let size: Int64 = entries.size
for (i in 0..size) {
if (entries[i].hash >= 0) {
action(entries[i].key, entries[i].value)
}
}
}
/**
* @description Determine whether all key-value pairs in the HashMap satisfy the condition.
*
* @param predicate the given condition.
* @returns true if all key-value pairs in the HashMap satisfy the condition, otherwise returns false.
*/
@When[env != "ohos"]
public func all(predicate: (K, V) -> Bool): Bool {
let size: Int64 = entries.size
for (i in 0..size) {
if (entries[i].hash >= 0 && !predicate(entries[i].key, entries[i].value)) {
return false
}
}
return true
}
/**
* @description Determine whether there is any key-value pair in the HashMap that satisfies the condition.
*
* @param predicate the given condition.
* @returns Whether there is any key-value pair that satisfies the condition.
*/
@When[env != "ohos"]
public func any(predicate: (K, V) -> Bool): Bool {
let size: Int64 = entries.size
for (i in 0..size) {
if (entries[i].hash >= 0 && predicate(entries[i].key, entries[i].value)) {
return true
}
}
return false
}
/**
* @description Determine whether all key-value pairs in the HashMap do not satisfy the condition.
*
* @param predicate the given condition.
* @returns Whether all key-value pairs in the current HashMap do not satisfy the condition.
*/
@When[env != "ohos"]
public func none(predicate: (K, V) -> Bool): Bool {
let size: Int64 = entries.size
for (i in 0..size) {
if (entries[i].hash >= 0 && predicate(entries[i].key, entries[i].value)) {
return false
}
}
return true
}
/**
* @description Computes from left to right using the specified initial value.
*
* @param initial the given initial value of type R.
* @param operation the given computation function.
* @returns the final computed value.
*/
@When[env != "ohos"]
public func fold<R>(initial: R, operation: (R, K, V) -> R): R {
var result: R = initial
let size: Int64 = entries.size
for (i in 0..size) {
if (entries[i].hash >= 0) {
result = operation(result, entries[i].key, entries[i].value)
}
}
return result
}
/**
* @description Compute from left to right using the first value as the initial value.
*
* @param operation the given computation function.
* @returns the final computed value.
*/
@When[env != "ohos"]
public func reduce(operation: (V, V) -> V): Option<V> {
if (this.size == 0) {
return None<V>
}
let len: Int64 = entries.size
var index = 0
var result = unsafe { zeroValue<V>() }
while (index < len) {
if (entries[index].hash >= 0) {
result = entries[index].value
index++
break
}
index++
}
if (this.size == 1) {
return result
}
while (index < len) {
if (entries[index].hash >= 0) {
result = operation(result, entries[index].value)
}
index++
}
return result
}
/**
* An exception is reported when the get operator is overloaded and the key does not exist.
*
* @param key transfer the value for judgment.
* @return the value corresponding to the key.
*
* @throws NoneValueException if value does not exist.
*
* @since 0.18.4
*/
@Frozen
public operator func [](key: K): V {
return match (get(key)) {
case None => throw NoneValueException("Value does not exist!\n")
case Some(val) => val
}
}
/**
* The operator overloads the set. If the key does not exist, an exception is reported.
*
* @param key transfer the value for judgment.
* @param value transfer the value to be set.
*
* @since 0.18.4
*/
@Frozen
public operator func [](key: K, value!: V): Unit {
add(key, value)
}
/*
* Transfer the key to obtain the k index.
*
* @param hash transfer the value for judgment.
* @param size transfer the value for judgment.
*
* @return the k index corresponding to the key.
*
* @since 0.18.4
*/
@Frozen
@OverflowWrapping
func getIndex(hash: Int64, size: Int64): Int64 {
return hash & (size - 1)
}
/*
* Transfer the key to obtain the hash value.
*
* @param key transfer the value for judgment.
* @return the hash value corresponding to the key.
*
* @since 0.18.4
*/
@Frozen
@OverflowWrapping
private func getHash(hashCode: Int64): Int64 {
hashCode ^ (hashCode >> 32)
}
/**
* Returns sizes of key-value.
*
* @return sizes of key-value.
*
* @since 0.18.4
*/
@Frozen
public prop size: Int64 {
get() {
return appendIndex - freeSize
}
}
/**
* Returns iterator of hashmap.
*
* @return iterator of hashmap.
*
* @since 0.18.4
*/
@Frozen
public func iterator(): HashMapIterator<K, V> {
return HashMapIterator<K, V>(this)
}
/**
* Check whether the size is empty. If yes, true is returned. Otherwise, false is returned.
*
* @return bool if yes, true is returned. Otherwise, false is returned.
*
* @since 0.18.4
*/
@Frozen
public func isEmpty(): Bool {
return (appendIndex - freeSize) == 0
}
/**
* Obtaining the Version Number.
*
* @return the modCount about technology.
*
* @since 0.18.4
*/
@Frozen
func version(): Int64 {
return modCount
}
@Frozen
private func max(l: Int64, r: Int64) {
return if (l >= r) {
l
} else {
r
}
}
/**
* Returns the element in this Map as an Array.
*/
@Frozen
public func toArray(): Array<(K, V)> {
let arr = Array<(K, V)>(size, repeat: unsafe { (zeroValue<K>(), zeroValue<V>()) })
var index = 0
var pos = 0
while (index < size) {
if (entries[pos].hash >= 0) {
arr[index] = (entries[pos].key, entries[pos].value)
index++
}
pos++
}
return arr
}
/*
* Initializes or doubles the table size.
* If empty, the allocation is based on the initial DEFAULT_CAPACITY target reserved in the field threshold.
*
* @return a TreeNode encapsulated with option after expansion.
*
* @since 0.18.4
*/
@Frozen
@OverflowWrapping
func resize(argCap: Int64): Bool {
var isRehash: Bool = false
let oldBucketsSize = buckets.size
let allowedBucketSize = Int64(Float32(oldBucketsSize) * LOAD_FACTOR)
// Except for reserve, following two if condition would not be satisfied at the same time.
if (argCap >= allowedBucketSize) {
let newCapOfBuckets = max(oldBucketsSize << 1, tableSizeFor(argCap))
let newBuckets: Array<Int64> = Array<Int64>(newCapOfBuckets, repeat: -1)
for (i in 0..entries.size) {
if (entries[i].hash >= 0) {
let bucket: Int64 = getIndex(entries[i].hash, newCapOfBuckets)
entries[i] = HashMapEntry<K, V>(entries[i].hash, newBuckets[bucket], entries[i].key,
entries[i].value)
newBuckets[bucket] = i
}
}
buckets = newBuckets
isRehash = true
}
if (argCap > entries.size) {
let oldEntriesSize = entries.size
let newCapOfEntries = max(oldEntriesSize + (oldEntriesSize >> 1), argCap)
let newEntries = Array<HashMapEntry<K, V>>(
newCapOfEntries,
repeat: HashMapEntry<K, V>()
)
entries.copyTo(newEntries, 0, 0, oldEntriesSize)
entries = newEntries
}
return isRehash
}
/*
* Returns a power of two size for the given target capacity.
*
* @return target capacity.
*
* @since 0.18.4
*/
@Frozen
private static func tableSizeFor(cap: Int64): Int64 {
if (cap <= DEFAULT_CAPACITY) {
return DEFAULT_CAPACITY
}
if (cap < MAX_BUCKET_SIZE) {
var n: Int64 = cap - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
n |= n >> 32
return n + 1
} else {
return MAX_BUCKET_SIZE
}
}
@Frozen
func removePosition(hash: Int64, pos: Int64) {
let index: Int64 = getIndex(hash, buckets.size)
var bucket: Int64 = buckets[index]
var pre: Int64 = -1
while (bucket != pos) {
pre = bucket
bucket = entries[bucket].next
}
if (pre < 0) {
buckets[index] = entries[pos].next
} else {
entries[pre] = HashMapEntry<K, V>(entries[pre].hash, entries[pos].next, entries[pre].key, entries[pre].value)
}
entries[pos] = HashMapEntry<K, V>(-1, freeIndex, unsafe { zeroValue<K>() }, unsafe { zeroValue<V>() })
freeIndex = bucket
freeSize++
modCount++
}
}
/**
*
*
* @since 0.18.4
*/
extend<K, V> HashMap<K, V> <: ToString where V <: ToString, K <: ToString {
@Frozen
public func toString(): String {
if (size == 0) {
return "[]"
}
let sb = StringBuilder("[")
let it = iterator()
var tmp: (K, V) = it.next().getOrThrow()
while (let Some(next) <- it.next()) {
//"(${k}, ${v})"
sb.append(r'(')
sb.append(tmp[0])
unsafe { sb.appendFromUtf8Unchecked(", ".toArray()) }
sb.append(tmp[1])
unsafe { sb.appendFromUtf8Unchecked("), ".toArray()) }
tmp = next
}
sb.append(r'(')
sb.append(tmp[0])
unsafe { sb.appendFromUtf8Unchecked(", ".toArray()) }
sb.append(tmp[1])
unsafe { sb.appendFromUtf8Unchecked(")]".toArray()) }
return sb.toString()
}
}
/**
* This is the function of hashmap equal and not equal. If the keys and values are same for two hasmaps, then they are equal.
*
* Returns true if the two hashmaps are equal for ==. Return true if the two hashmaps are not equal for !=.
*
* @since 0.18.4
*/
extend<K, V> HashMap<K, V> <: Equatable<HashMap<K, V>> where V <: Equatable<V> {
@Frozen
public operator func ==(right: HashMap<K, V>): Bool {
if (refEq(this, right)) {
return true
}
if (size != right.size) {
return false
}
for ((leftKey, leftValue) in this) {
var a = match (right.get(leftKey)) {
case Some(v) => v
case None => return false
}
if (a != leftValue) {
return false
}
}
return true
}
@Frozen
public operator func !=(right: HashMap<K, V>): Bool {
if (size != right.size) {
return true
}
for ((leftKey, leftValue) in this) {
var a = match (right.get(leftKey)) {
case Some(v) => v
case None => return true
}
if (a != leftValue) {
return true
}
}
return false
}
}
/**
* This is a hashmap iterator used to iterate effects.
*
* @since 0.18.4
*/
public class HashMapIterator<K, V> <: Iterator<(K, V)> where K <: Hashable & Equatable<K> {
/* Define an empty set */
private var lockVersion: Int64
private var index: Int64
private let data: HashMap<K, V>
private var isRemoved: Bool
/**
* Initialize the iterator and transfer the hashmap.
*
* @param map the hashmap to be transferred.
*
* @since 0.18.4
*/
@Frozen
public init(map: HashMap<K, V>) {
isRemoved = true
data = map
lockVersion = map.version()
index = -1
}
/**
* Iterates the bucketIndex element. The return type is option, which contains key and value.
*
* @return type is option, which contains key and value.
*
* @throws ConcurrentModificationException if lockVersion is not equal to "data.version()".
*
* @since 0.18.4
*/
@Frozen
public func next(): ?(K, V) {
if (lockVersion != data.version()) {
throw ConcurrentModificationException()
}
let size: Int64 = data.entries.size
do {
index++
} while (index < size && data.entries[index].hash < 0)
if (index >= size) {
return Option<(K, V)>.None
}
isRemoved = false
return Some((data.entries[index].key, data.entries[index].value))
}
/**
* Remove the element returned by the next function of this iterator.
* This method can be called only once when the next function is called.
*
* @see Currently, the Cangjie modifier does not implement the function visible in the package,
* and the API function modified by public is insecure. It is recommended that this method be used temporarily.
* This method can be improved after the function visible in the package is supported in the future.
* In the future, the function should be used to implement the function.
*
* @throws ConcurrentModificationException if lockVersion is not equal to "data.version()".
*
* @since 0.24.1
*/
@Frozen
public func remove(): Option<(K, V)> {
if (lockVersion != data.version()) {
throw ConcurrentModificationException()
}
if (isRemoved || data.size <= 0 || index >= data.entries.size || data.entries[index].hash < 0) {
return None<(K, V)>
}
let item = data.entries[index]
data.removePosition(item.hash, index)
isRemoved = true
lockVersion = data.version()
return Some((item.key, item.value))
}
}