A HashMap is a specialized data structure that contains key-value pairs.
HashMaps provide (near) constant-time lookup for keys, making them highly efficient at finding the value associated with a specific key - even if there is a very large amount of keys.
See Wikipedia to learn more about the underlying technology.
This section introduces the HashMap and its features and is intended to help create a basic understanding of what HashMaps are and what they can be used for.
Comparison with Arrays
The HashMap and the Array are both data structures that are used to store multiple elements. Arrays are simple and only store single elements, one after the other, while HashMaps store key-value pairs, where each key uniquely identifies a single value.
Unlike Arrays, HashMaps have no order, i.e. there is no such thing as a first, second, third or last element in a HashMap. As such, the # and select commands can not be used with HashMaps. This also means that the order in which the forEach-loop processes a HashMap's key-value pairs is not deterministic.
Iterating over all elements in a HashMap is less efficient than doing the same in an Array.
Constant-time Key Lookup
HashMaps are specifically designed for (near) constant-time lookup of keys.
Consider the following example to understand what that means: Let's say we have an Array that looks like this:
But what findIf actually does for us is something like this:
Now consider how many isEqualTo comparisons this forEach-loop has to perform: If the element identified by "Wanted_UID" is stored at or near the end of _playerDataArray, then our code has to go through (almost) the entire Array to find it - and the same is the case if the element we are looking for does not exist in our Array.
Every single one of these comparisons takes some time - not much, but it will add up eventually. This is no problem as long as our Array is relatively small, but it becomes a serious issue when the Array starts growing: If there are 10,000 elements in the Array that means up to 10,000 comparisons might be necessary just to find a single element, if there are 100,000 elements that means up to 100,000 comparisons and so on - the amount of steps and time needed to find an element grows linearly with the Array size.
And this is where HashMaps come in. Simply put: When a key-value pair is inserted into a HashMap, a hash function is applied to the key to determine the position at which the key-value pair is going to be stored. Then, when we want to retrieve the value associated with a specific key, the same hash function (Arma 3 uses FNV-1a 64-bit) is applied to that key and the resulting position tells the HashMap exactly where to find the key-value pair that we are looking for.
Since the process to find a given key-value pair is always the same (apply hash function, look at the resulting position, return the value that is stored there), it also always takes (more or less) the same amount of time - regardless of the size of the HashMap.
That is what constant-time lookup for keys means. And it comes with very useful benefits: The procedures to search for, retrieve, modify or remove a specific element can all be completed in constant time; whether the HashMap contains 10, 10,000 or 1,000,000 elements does not matter.
Working with HashMaps
Supported Key Types
Because of the requirement for the keys to be hashable (and constant), not all Data Types can be used as keys.
Supported types are limited to:
Creating a HashMap
Setting an element
Inserting an element with a key that already exists inside the HashMap will overwrite the existing key.
Getting an element
Values are retrieved by their key:
Removing an element
Elements can be removed (deleted) from the HashMap using deleteAt with the element's key:
Checking if an element exists
It is possible to check if a key is present in the HashMap using the in command:
The count command can be used to return the number of key-value pairs stored in the HashMap:
A HashMap variable is a reference to the HashMap (see Wikipedia); this means that if the HashMap is edited, all scripts and functions using this HashMap will see the changes.
A HashMap set through setVariable does not need to be assigned again:
Copying a HashMap
In order to avoid this behaviour, copy the HashMap with + (plus):
Arrays stored as key or value in the HashMap will also be deep-copied.
Iterating through a HashMap
Merging two HashMaps
Two HashMaps can be merged together using merge.
Unsupported Key Types
The above example can be extended to add support for iterations, deletion, etc. as well.
Keys can be prefixed by an underscore so that the HashMap can be converted to private variables on-the-fly using params. This can be useful if a HashMap is kept alive for a long time and its values need to be accessed often, or to further simplify passing data between computers. Here's an example with a simple vehicle respawn system:
Scalar Key Precision
Numbers in Arma 3 are floating point numbers, and because there are gaps between floating point numbers, rounding is necessary - see also Wikipedia. For example, 87654316, 87654317, 87654318, 87654319, 87654320, 87654321, 87654322, 87654323 and 87654324 will all be rounded to and treated as the same value by the game engine (because the actual value of each of these numbers can not be represented as an Arma 3 floating point number). Similar problems occur with fractional numbers:
This means that using very large numbers or fractional numbers as HashMap keys has to be done cautiously to avoid accidentally overwriting existing keys.