1. Introduction
HashMaps are incredibly useful data structures in Java, allowing us to store key-value pairs that enable fast and efficient data retrieval. As developers, we must understand the process of initializing HashMaps to harness their power in our projects effectively.
There are a few different ways to initialize a HashMap in Java. One common approach is using the default constructor, which automatically sets the data structure’s initial capacity and load factor. Another method involves a static initializer block, which lets us create and populate the HashMap with data beforehand. Throughout this article, we’ll explore these methods alongside code examples to guide you in mastering this topic in Java.
2. Understanding HashMaps in Java
Java HashMap is a part of java.util package and provides an implementation of the Map interface. It stores data in key-value pairs, allowing you to access them by specifying an index of another data type (e.g., an Integer). HashMaps offer a fast and efficient way to manage and organize data.
Let’s look at a simple example:
import java.util.HashMap; public class HashMapExample { public static void main(String[] args) { HashMap<Integer, String> employeeMap = new HashMap<>(); employeeMap.put(1, "John"); employeeMap.put(2, "Jane"); employeeMap.put(3, "Alice"); System.out.println(employeeMap.get(2)); // Output: Jane } }
2.1. The Map Interface
Java HashMap class extends the AbstractMap class and implements the Map
interface. The Map
interface provides a collection of key-value pairs, each unique and associated with a value. HashMap inherits numerous methods from the Map
interface, such as put, get, keySet, values, and entrySet.
2.2. Key-Value Pairs
A HashMap key-value pair is analogous to a dictionary entry in which the definition(value) may be accessed using a word(key). It enables fast retrieval of values via their related keys. Here’s an illustration:
HashMap<String, String> countryCapitalMap = new HashMap<>(); countryCapitalMap.put("France", "Paris"); countryCapitalMap.put("Germany", "Berlin"); countryCapitalMap.put("USA", "Washington D.C."); System.out.println(countryCapitalMap.get("Germany")); // Output: Berlin
2.3. Null Key Handling
You can have one null key and unlimited null values in Java HashMaps. When a null key is used, the HashMap saves the value in a specific bucket, allowing you to store and retrieve data using a null key.
HashMap<String, String> nullKeyMap = new HashMap<>(); nullKeyMap.put(null, "Null Key Value"); System.out.println(nullKeyMap.get(null)); // Output: Null Key Value
3. Initialize a HashMap in Java
When you want to initialize HashMap in Java, there are multiple ways. We will cover Initial Capacity and Load Factor, Empty Map, Using Constructor, Static Initializer and Streams API.
3.1. Initial Capacity and Load Factor
When creating a HashMap, we can define its initial capacity and load factor. The initial capacity indicates the number of buckets in the HashMap, while the load factor determines when the HashMap should be resized. The default load factor is 0.75, meaning it will resize when 75% of the capacity is occupied.
import java.util.HashMap; // initial capacity: 10, load factor: 0.8 HashMap<String, String> customHashMap = new HashMap<>(10, 0.8f);
3.2. Empty Map
Creating an empty HashMap is simple. The default constructor initializes the HashMap with an initial capacity of 16 and a load factor of 0.75:
import java.util.HashMap; HashMap<String, String> emptyHashMap = new HashMap<>();
3.3. Static Initializer
A HashMap can be initialized using a static block initializer, which is particularly useful for creating a static HashMap object.
import java.util.HashMap; import java.util.Map; public class MyHashMap { public static Map<String, String> articleMap; static { articleMap = new HashMap<>(); articleMap.put("ar01", "Intro to Map"); articleMap.put("ar02", "Some article"); } }
3.4. Using Java 8 Streams
Stream API has a different method for sequential and parallel collection processing. The Collectors static utility class can covert to a map using toMap() method.
3.4.1. Stream Collection to Map
private static void usingStreamsOnCollection() { Book one = new Book("myBook", 400); Book two = new Book("newBook", 300); List<Book> list = List.of(one, two); Map<String, Integer> map = list.stream() .collect(Collectors.toMap(Book::getName, Book::getPages)); }
In this example, we created a few new objects and applied stream() to dynamically process each element. The collect with toMap() method converts each record into a key-value map entry.
With this dynamic approach, we can efficiently process a collection of any size.
3.4.2. Using StreamOf()
private static void usingStreamOf() { Map<String, Integer> map = Stream.of( new Book("myBook", 400), new Book("newBook", 300)) .collect(Collectors.toMap(Book::getName, Book::getPages)); }
The StreamOf() approach is very similar, but here, we have supplied a list of pre-defined values instead. This approach can be very tiring, depending on the number of elements needed.
Perhaps it could make sense when only a fixed number of elements exist.
3.5. Java 9 MapOf() And MapOfEntries()
Java 9 has introduced factory methods to the Map interface that can provide alternatives to the existing Map initialization methods. MapOf() and MapOfEntries() return an immutable result, so any try-on modification will result in UnsupportedOperationException.
private static void usingMapOfAndOfEntries() { Map<String, Integer> mapOf = Map.of("myBook", 400, "newBook", 300); Map<String, Integer> mapOfEntries = Map.ofEntries( new AbstractMap.SimpleEntry<>("myBook", 400), new AbstractMap.SimpleEntry<>("newBook", 300), new AbstractMap.SimpleEntry<>("hisBook", 500) ); }
Map.of() has a limit of up to 10 map entries because it’s not a varargs method.
When using the Map.ofEntries(), there are no limitations in terms of entries. Both factory method doesn’t allow duplicate or null elements.
3.6. Using Guava
Google Guava provides similar methods to the Java 9 factory methods. The ImmutableMap.of() has a limit of 5 entries; otherwise, you must use its builder method.
private static void usingGoogleGuava() { Map<String, Integer> map = ImmutableMap.of("Book", 400, "nBook", 300); Map<String, Integer> mapTwo = ImmutableMap.<String, Integer>builder() .put("Book", 400) .put("nBook", 300) .build(); }
3.7. Using Collections.singletonMap()
Sometimes, we only need a map with a single key-value pair, and using a full-blown HashMap might be overkill. For this scenario, Java provides the Collections.singletonMap() method, which creates an immutable map containing only the specified key-value pair.
Map<String, String> singleEntryMap = Collections.singletonMap("usa", "John Doe");
Remember that using Collections.singletonMap() returns an immutable singleton map, which means you cannot update or add new map entries.
4. Immutable and Mutable Maps
It’s critical to distinguish between mutable and immutable maps when dealing with maps in Java. Mutable maps enable us to add, remove, and alter items, whereas immutable maps are read-only and cannot be changed after they have been formed.
AbstractMap is a typical basic class for mutable maps in Java. It is an abstract class that provides the Map interface’s basic implementation. Using the HashMap implementation, we can create a mutable map.
import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, String> mutableMap = new HashMap<>(); mutableMap.put("Title", "My New Article"); mutableMap.put("Title2", "Second Article"); System.out.println(mutableMap); } }
4.1. Immutable Maps in Java 9
Map.of(), a new convenience factory method introduced in Java 9, was added to generate immutable maps. This convenient way enables us to generate compact, immutable maps without needing a separate library or additional code. When using this method, remember that values and keys cannot be null. Here’s a basic example of using Map.of() to create an immutable map in Java 9:
import java.util.Map; public class Main { public static void main(String[] args) { Map<String, String> immutableMap = Map.of( "Title", "My New Article", "Title2", "Second Article" ); System.out.println(immutableMap); } }
In conclusion, we’ve reviewed the distinctions between mutable and immutable maps in Java, produced a mutable map with AbstractMap, and demonstrated how to create an immutable map with Java 9’s Map.of() function.
When working with maps in Java, it’s critical to determine whether a mutable or immutable map is the best data structure for your application’s needs.
Immutable maps are often more efficient and help minimise errors caused by unintentional alterations, but mutable maps allow more flexibility when changing the data structure.
5. Adding Elements to a HashMap
Java 9 introduced the Map.of and Map.ofEntries methods, which allow us to create unmodifiable maps with a more concise syntax. Let’s take a look at how we can use both methods to add elements to a HashMap:
Map<String, Integer> myMap = Map.of("One", 1, "Two", 2, "Three", 3);
With Map.of, you can create an unmodifiable map with up to 10 key-value pairs. You cannot modify the map once it is created. Remember that if you try to modify it, an UnsupportedOperationException will be thrown.
Map<String, Integer> myMap = Map.ofEntries( Map.entry("One", 1), Map.entry("Two", 2), Map.entry("Three", 3) );
The Map.ofEntries method allows you to create an unmodifiable map with more than ten key-value pairs. Similarly to Map.of, the map cannot be modified once created.
5.1. Adding Key-Value Pairs with Java 8
Java 8 provided helpful features, such as computeIfAbsent, to make adding key-value pairs to a HashMap easier. In particular, computeIfAbsent helps prevent overriding existing values when adding new elements.
Map<String, Integer> myMap = new HashMap<>(); myMap.computeIfAbsent("One", key -> 1); myMap.computeIfAbsent("Two", key -> 2); myMap.computeIfAbsent("Three", key -> 3);
6. Summary
Ultimately, HashMaps are an important Java data structure that allows for the efficient storage and retrieval of key-value pairs. To create and manipulate HashMaps in Java, you must first understand the Java HashMap class and the Map interface.
When creating a HashMap, it is also important to consider the initial capacity and load factor and the distinction between immutable and mutable maps. While AbstractMap and mutable maps allow for modification, immutable maps in Java 9 allow for creating read-only HashMaps.
Daniel Barczak
Daniel Barczak is a software developer with a solid 9-year track record in the industry. Outside the office, Daniel is passionate about home automation. He dedicates his free time to tinkering with the latest smart home technologies and engaging in DIY projects that enhance and automate the functionality of living spaces, reflecting his enthusiasm and passion for smart home solutions.
Leave a Reply