HashMap

To use the HashMap type, you need to import the collection package.

import std.collection.*

You can use the HashMap type to construct a collection of key-value pairs, where each key is unique.

A HashMap is a hash table that provides quick access to elements contained in it. Each element in the table is identified by its key, and the key can be used to access the corresponding value.

HashMap is a generic type, so Cangjie uses HashMap<K, V> to denote it. K is the key type of the HashMap. It must be a type that implements the Hashable and Equatable<K> interfaces, for example, a numeric type or String, or a type variable with the respective constraints. V is the value type of the HashMap, which can be any type.

var a: HashMap<Int64, Int64> = ... // HashMap whose key type is Int64 and value type is Int64
var b: HashMap<String, Int64> = ... // HashMap whose key type is String and value type is Int64

Two HashMap instantiations with different key and/or value types are different, incompatible types, so their values cannot be "cross-assigned". Continuing the last example, the following code is invalid:

b = a // Type mismatch

In Cangjie, you use a constructor to create a HashMap with particular key and value types. The different constructors enable you to pre-allocate storage and/or initialize the elements:

// Create an empty HashMap that maps strings to Int64 integers:
let a = HashMap<String, Int64>()

// Create a HashMap that maps strings to Int64 integers,
// and populate it with key-value pairs ("a", 0), ("b", 1), and ("c", 2):
let b = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])

// Use a collection of 2-tuples of type (String, Int64) to populate a HashMap:
let c = HashMap<String, Int64>(b)

// Create an empty HashMap that maps strings to Int64 integers
// with the initial capacity of 10:
let d = HashMap<String, Int64>(10)

// Create a HashMap with Int64 being both the key and value type
// and add ten key-value pairs to it using a rule function:
let e = HashMap<Int64, Int64>(10, {x: Int64 => (x, x * x)})

Accessing HashMap Members

When you need to access each key-value pair of a HashMap, you can use the for-in loop expression to traverse it.

import std.collection.*

main() {
    let map = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])
    for ((k, v) in map) {
        println("The key is ${k}, the value is ${v}")
    }
}

It should be noted that a HashMap does not guarantee that its elements are arranged in the order in which they were inserted, or in any other particular order. Therefore, the traversal order may be different from the insertion order.

Compiling and executing the preceding code will output the following lines:

The key is a, the value is 0
The key is b, the value is 1
The key is c, the value is 2

but not necessarily in the same order.

When you need to know the number of elements in a HashMap, you can use its size property to obtain that information.

import std.collection.*

main() {
    let map = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])
    if (map.size == 0) {
        println("This is an empty hashmap")
    } else {
        println("The size of hashmap is ${map.size}")
    }
}

Compiling and executing the preceding code outputs the following:

The size of hashmap is 3

When you want to determine whether there is a key-value pair with the given key in a HashMap, you can use its contains member function. If a pair with the key k is present in the HashMap, contains(k) returns true, otherwise, it returns false.

let map = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])
let a = map.contains("a") // a == true
let b = map.contains("d") // b == false

When you want to access the value corresponding to the given key, you can use the index operator [ ] (the index expression must be of the key type). Using a key that is not present in the HashMap as an index triggers a run-time exception.

let map = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])
let a = map["a"] // a == 0
let b = map["b"] // b == 1
let c = map["d"] // Runtime exception

Modifying a HashMap

HashMap is a mutable type. It provides functions for modifying, adding, and deleting elements.

You can use the index operator [ ] to modify the value of a key:

let map = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])
map["a"] = 3

To add a single key-value pair to a HashMap, use its put member function. To add multiple key-value pairs at the same time, use the putAll member function. If a pair with the given key is not already present in the HashMap, the put function adds it. Otherwise, the put function overwrites the old value associated with that key with the new value.

let map = HashMap<String, Int64>()
map.put("a", 0) // map contains the element ("a", 0)
map.put("b", 1) // map contains the elements ("a", 0), ("b", 1)
let map2 = HashMap<String, Int64>([("c", 2), ("d", 3)])
map.putAll(map2) // map contains the elements ("a", 0), ("b", 1), ("c", 2), ("d", 3)

In addition to using the put function, you can also directly add a new key-value pair to a HashMap, or update the value already associated with the given key, by using the index operator [ ] in an assignment expression:

let map = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])
map["d"] = 3 // map contains the elements ("a", 0), ("b", 1), ("c", 2), ("d", 3)

HashMap is a reference type. When a HashMap is used as an expression, no copy is created. All references to the same HashMap instance share the same data.

Therefore, modifications of a HashMap instance affect all references to that instance:

let map1 = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2)])
let map2 = map1
map2["a"] = 3
// map1 contains the elements ("a", 3), ("b", 1), ("c", 2)
// map2 contains the elements ("a", 3), ("b", 1), ("c", 2)

To delete an element from a HashMap, you can use its remove member function to specify the key to be deleted from the HashMap along with the associated value. If there is no such key, nothing happens.

let map = HashMap<String, Int64>([("a", 0), ("b", 1), ("c", 2), ("d", 3)])
map.remove("d") // map contains the elements ("a", 0), ("b", 1), ("c", 2)