Background
R was the first programming language I learned while studying Economics, and it’s what originally drew me closer to Computer Science. When I worked as a Data Analyst at SGEPT, I encountered a painstakingly slow piece of code that desperately needed efficient, vectorized key-value lookups. R has plenty of vectorized functions, but I could not find a fast, vectorized hashmap.
This led to hashmapR, a flexible, vectorized hashmap implemented in C++.
Why Another Hashmap?
Technically R already has some form of a hashmap (environments) but they come with significant drawbacks:
- String-only keys: Environments only accept string keys, and each string is interned into the global symbol table permanently. Managing multiple large maps can quickly fill up memory.
- Poor bulk operation performance: Vectorized operations in existing solutions are often implemented by looping (in R) over a function that executes each operation individually — the overhead kills performance at scale.
hashmapR addresses both:
- Flexible keys and values: Any serializable R object works as a key or value
- True vectorization: Bulk operations execute natively in C++, completely bypassing R’s interpreter
Implementation
hashmapR wraps a C++ std::unordered_map. Each key and value is stored in a growable R list, preventing premature garbage collection.
Hashing is type-aware:
- Primitives (strings, integers, floats) are extracted and hashed directly via
std::hash - Complex objects are serialized to bytes, then hashed
This flexibility means you can use virtually any R object as a key. The real performance win comes from native vectorization — bulk operations execute entirely in C++, bypassing R’s interpreter overhead completely.
R’s copy-on-write semantics mean inserted objects aren’t deep-copied: only references are stored. This is worth remembering if you store non-COW objects.
Key Features
- Type Flexibility: Use any serializable R object as a key or value
- Serialization: Save and restore maps easily:
saveRDS(map$to_list(), "my_map.rds") map_restored <- hashmap()$from_list(readRDS("my_map.rds")) - Utility Operations: Invert, query size, clear, clone, and more
Getting Started
Install from CRAN:
install.packages("hashmapR")
Or the development version:
devtools::install_github("svensglinz/hashmapR")
Usage is straightforward:
library(hashmapR)
# Create and populate a hashmap
map <- hashmap()
map["user_1"] <- "Alice"
map["user_2"] <- "Bob"
# Single lookup
map["user_1"] # "Alice"
# Vectorized bulk operations
map$set(list("user_1", "user_2"), list("Alice", "Bob"), vectorize=TRUE)
map$get(list("user_1", "user_2"), vectorize=TRUE)
# Utility functions
map$contains("user_1") # TRUE
map$size() # 2
map$clear()
# create a new, inverted map, [A -> C, B -> C ] ==> [C -> list(A, B)]
map$invert(duplicates = "stack")
Benchmarks
Coming Soon
hashmapR is available on CRAN and on GitHub. Contributions and issues are welcome!