The Speed Test: Comparing Map.of() and new HashMap() in Java

Java is a popular programming language used for developing a wide range of applications, including web, mobile, and desktop applications.

It provides many useful data structures for developers to use in their programs, one of which is the Map interface.

The Map interface is used to store data in key-value pairs, making it an essential data structure for many applications.

In this article, we will discuss the use of Map.of() and new HashMap<>() in Java, the difference between them, and the benefits of using Map.of().

What is Map.of()?

Map.of() is a method introduced in Java 9, which allows developers to create an immutable map with up to 10 key-value pairs.

It provides a convenient and concise way of creating maps, making it easier to create small maps without having to write a lot of code. Map.of() is an improvement over the previous way of creating small maps using the constructor of the HashMap class, which can be cumbersome and verbose.

What is new HashMap<>()?

new HashMap<>() is a constructor provided by the HashMap class in Java, which allows developers to create a new instance of a HashMap. It is used to create a mutable map, which means that the map can be modified by adding, removing, or updating key-value pairs.

It is a commonly used method for creating maps in Java, especially when dealing with larger sets of data.

Benchmarking Map.of() and new HashMap<>()

To compare the performance of Map.of() and new HashMap<>() in Java, we can use benchmarking tools to measure the time taken to perform various operations on maps created using these methods. In our benchmark, we will measure the time taken to get a value from a map and the time taken to insert values into a map.

t’s worth noting that our benchmarks are constrained to a limited and small set of data, such as ten items It’s possible that the results could differ for larger data sets or more complex use cases.

The benchmarking code

package ca.bazlur;

import org.openjdk.jmh.annotations.Benchmark;

import org.openjdk.jmh.annotations.*;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 10, time = 1)
@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MapBenchmark {
private static final int SIZE = 10;

private Map<Integer, String> mapOf;
private Map<Integer, String> hashMap;

@Setup
public void setup() {
mapOf = Map.of(
0, “value0”,
1, “value1”,
2, “value2”,
3, “value3”,
4, “value4”,
5, “value5”,
6, “value6”,
7, “value7”,
8, “value8”,
9, “value9”
);

hashMap = new HashMap<>();
for (int i = 0; i < SIZE; i++) {
hashMap.put(i, “value” + i);
}
}

@Benchmark
public void testMapOf() {
Map<Integer, String> map = Map.of(
0, “value0”,
1, “value1”,
2, “value2”,
3, “value3”,
4, “value4”,
5, “value5”,
6, “value6”,
7, “value7”,
8, “value8”,
9, “value9”
);
}

@Benchmark
public void testHashMap() {
Map<Integer, String> map = new HashMap<>();
for (int i = 0; i < SIZE; i++) {
map.put(i, “value” + i);
}
}

@Benchmark
public void testGetMapOf() {
for (int i = 0; i < 10; i++) {
mapOf.get(i);
}
}

@Benchmark
public void testGetHashMap() {
for (int i = 0; i < SIZE; i++) {
hashMap.get(i);
}
}
}

The results

Benchmark Mode Cnt Score Error Units
MapBenchmark.testGetHashMap avgt 10 15.245 ± 0.751 ns/op
MapBenchmark.testGetMapOf avgt 10 16.263 ± 0.348 ns/op
MapBenchmark.testHashMap avgt 10 100.133 ± 1.109 ns/op
MapBenchmark.testMapOf avgt 10 66.272 ± 0.285 ns/op

These are the benchmark results for comparing the performance of using new HashMap<>() and Map.of() in Java. The benchmark was conducted with a limited and small set of data (e.g. 10) and the result may vary based on the JDK distribution and the computer configuration.

The testGetHashMap benchmark compares the time it takes to retrieve a value from a HashMap instance using the get method. The average time it takes for 10 iterations is 15.245 nanoseconds with a standard deviation of 0.751.

The testGetMapOf benchmark compares the time it takes to retrieve a value from a Map.of() instance using the get method. The average time it takes for 10 iterations is 16.263 nanoseconds with a standard deviation of 0.348.

The testHashMap benchmark compares the time it takes to put 10 key-value pairs in a HashMap instance using the put method. The average time it takes for 10 iterations is 100.133 nanoseconds with a standard deviation of 1.109.

The testMapOf benchmark compares the time it takes to create a Map.of() instance with 10 key-value pairs. The average time it takes for 10 iterations is 66.272 nanoseconds with a standard deviation of 0.285.

These benchmark results show that in most cases, using Map.of() can provide a significant improvement in speed compared to using new HashMap<>(), particularly when creating a new map instance.

However, for retrieving values from an existing map instance, the performance difference between the two approaches is negligible.

Note that, based on your JDK distribution and computer, the benchmark results may slightly differ when you try them. However, in most cases, the results should be consistent. It’s always a good idea to run your own benchmarks to ensure you make the right choice for your specific use case.

Additionally, remember that micro-benchmarks should always be taken with a grain of salt and not used as the sole factor in making a decision. Other factors, such as memory usage, thread safety, and readability of code, should also be considered.

The source code can be found here: https://github.com/rokon12/java-map-jmh

Benefits of using Map.of()

There are several benefits to using Map.of() over the new HashMap<>() in Java:

Performance: As we have seen in our benchmark, Map.of() is significantly faster than new HashMap<>() for small maps. This can result in improved performance for applications that make use of small maps.

Conciseness: Map.of() provides a concise and convenient way of creating small maps in Java. This makes the code more readable and easier to maintain.

Immutability: Map.of() creates immutable maps, which means that once the map is created, it cannot be modified. This provides a degree of safety and security for the data stored in the map.

Type Safety: Map.of() provides type safety for the keys and values of the map, which helps prevent type-related errors that can occur when using new HashMap<>().

Conclusion

Map.of() is a powerful and useful method introduced in Java 9, which provides a faster and more concise way of creating small maps in Java.

Our benchmarking shows that Map.of() is significantly faster than new HashMap<>() for small maps.

The benefits of using Map.of() include improved performance, conciseness, immutability, and type safety.

Developers should consider using Map.of() when creating small maps in Java to take advantage of these benefits.

The post The Speed Test: Comparing Map.of() and new HashMap<>() in Java appeared first on foojay.