Default map value
In this post, I'll explain how to provide a default value when querying an absent key in a hash map in different programming languages.
Java
Let's start with Java, my first professional programming language.
In older versions, retrieving a value from a map required using the get()
method:
Map map = new HashMap(); //1
Object value = map.get(new Object()); //2
if (value == null) {
value = "default"; //3
}
Initialize an empty map
Attempt to retrieve a non-existent key
Assign a default value if the key is absent
With Java 1.8, the Map
interface introduced a more concise way to handle absent keys:
var map = new HashMap<Object, String>();
var value = map.getOrDefault(new Object(), "default"); //1
- Retrieve the value with a default in one step
Kotlin
Kotlin provides several approaches to retrieve values from a map:
get()
andgetOrDefault()
function just like their Java counterparts.getValue()
throws an exception if the key is missing.getOrElse()
accepts a lambda to provide a default value lazily.
val map = mapOf<Any, String>()
val default = map.getOrDefault("absent", "default") //1
val lazyDefault = map.getOrElse("absent") { "default" } //2
Retrieve the default value
Lazily evaluate the default value
Python
Python is less forgiving than Java when handling absent keys—it raises a KeyError
:
map = {}
value = map['absent'] #1
- Raises a
KeyError
To avoid this, Python offers the get()
method:
map = {}
value = map.get('absent', 'default') #1
Alternatively, Python's collections.defaultdict
allows setting a default for all absent keys:
from collections import defaultdict
map = defaultdict(lambda: 'default') #1
value = map['absent']
- Automatically provide a default value for any absent key
Ruby
Ruby's default behavior returns nil
for absent keys:
map = {}
value = map['absent']
For a default value, use the fetch
method:
map = {}
value = map.fetch('absent', 'default') #1
- Provide a default value for the absent key
Ruby also supports a more flexible approach with closures:
map = {}
value = map.fetch('absent') { |key| key } #1
- Return the queried key instead of a constant
Lua
My experience with Lua is relatively new, having picked it up for Apache APISIX. Let's start with Lua's map syntax:
map = {} --1
map["a"] = "A"
map["b"] = "B"
map["c"] = "C"
for k, v in pairs(map) do --2
print(k, v) --3
end
Initialize a new map
Iterate over key-value pairs
Print each key-value pair
Fun fact: the syntax for tables is the same as for maps:
table = {} --1
table[0] = "zero"
table[1] = "one"
table[2] = "two"
for k,v in ipairs(table) do --2
print(k, v) --3
end
Initialize a new map
Loop over the pairs of key values
Print the following:
1 one 2 two
Lua arrays start at index 0!
We can mix and match indices and keys. The syntax is similar, but there's no difference between a table and a map. Indeed, Lua calls the data structure a table:
something = {}
something["a"] = "A"
something[1] = "one"
something["b"] = "B"
for k,v in pairs(something) do
print(k, v)
end
The result is the following:
1 one
a A
b B
In Lua, absent keys return nil
by default:
map = {}
value = map['absent']
To provide a default, Lua uses metatables and the __index
metamethod:
Metatables allow us to change the behavior of a table. For instance, using metatables, we can define how Lua computes the expression
a+b
, wherea
andb
are tables. Whenever Lua tries to add two tables, it checks whether either of them has a metatable and whether that metatable has an__add
field. If Lua finds this field, it calls the corresponding value (the so-called metamethod, which should be a function) to compute the sum.
Each table in Lua may have its own metatable.
As I said earlier, when we access an absent field in a table, the result is nil. This is true, but it is not the whole truth. Such access triggers the interpreter to look for an
__index
metamethod: if there is no such method, as usually happens, then the access results in nil; otherwise, the metamethod will provide the result.
Here's how to use it:
table = {} --1
mt = {} --2
setmetatable(table, mt) --3
mt.__index = function (table, key) --4
return key
end
default = table['absent'] --5
Create the table
Create a metatable
Associate the metatable with the table
Define the
__index
function to return the absent keyThe
__index
function is called because the key is absent
Summary
This post explored how to provide default values when querying absent keys across various programming languages. Here's a quick summary:
Scope | Value | |||
Programming language | Per call | Per map | Static | Lazy |
Java | ❎ | ❌ | ❎ | ❌ |
Kotlin | ❎ | ❌ | ❎ | ❎ |
Python | ❎ | ❎ | ❌ | ❎ |
Ruby | ❎ | ❌ | ❎ | ❎ |
Lua | ❌ | ❎ | ❎ | ❌ |
Originally published at A Java Geek on August 11th, 2024
Subscribe to my newsletter
Read articles from Nicolas Fränkel directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Nicolas Fränkel
Nicolas Fränkel
Developer Advocate with 15+ years experience consulting for many different customers, in a wide range of contexts (such as telecoms, banking, insurances, large retail and public sector). Usually working on Java/Java EE and Spring technologies, but with focused interests like Rich Internet Applications, Testing, CI/CD and DevOps. Also double as a trainer and triples as a book author.