Welcome to an Introduction to Java HashMaps. Learn When and How to use HashMaps, HashMap features, basic methods along with with examples.
Tutorial Contents
Introduction to HashMap
HashMap is a hash table based implementation of the Map. In Java, Map is a key value based collection. Therefore Hashmap also stores the data in the form of key/value pairs.
HashMap is an unordered, and unsorted collection of key value pairs where a key is always Unique. HashSets which are unique collection of unsorted, unordered elements, actually use hashmap in the background. The Hash Maps use hashtable and bucketing logic for storing keys, where each key refers to an associated value.
When you want to retrieve an object out of HashMap, you have to pass the key. Based on the hashcode of the provided key, the underlying algorithms locates the key and returns the associated value.
HashMap, like any other hash based collections, provides constant in time operations like put, get, remove, contains etc. Because, the logic is based on hashcode and the size of the HashMap doesn’t affect the time it requires.
Major Features of HashMap
- HashMaps are key value collections where a key is always unique.
- They do not guarantee order or sorting when the elements are iterated.
- HashMap supports null key and null value. However, only one null key is allowed.
- You may duplicate the values any number of time.
- Most of the HashMap operations are constant in time, irrespective of size.
- HashMap does not specify the outcome, when you modify the key object, after it is inserted. For example, using array as a key and adding elements to the array. Modifying an object modifies its hashCode and hence shoud be avoided.
- Iterators on the HashMap are fail-fast and throws ConcurrentModificationException when the HashMap is modified, while an iterator is active.
- HashMap operations are not synchronized and you will have to synchronoize the threads accessing HashMap on you own.
- You can use Java 9, factory methods to create Immutable HashMaps in-line.
Examples of HashMap
Till now, we have covered some basic features of the HashMap. Before we go further, let’s try few exampels of using Hashmap.
Create and Print a Basic HashMap
You can create an Empty HashMap using the default Constructor and then add elements.
Map<String, String> hashMap = new HashMap<>();
hashMap.put("name", "Arya Starc");
hashMap.put("father", "Eddard Stark");
hashMap.put("mother", "Catelyn Stark");
hashMap.put("birthPlace", "Winterfell");
hashMap.put("theme", "The Needle");
System.out.println(hashMap.size()); // Output: 5
System.out.println(hashMap.containsKey("father")); // Output: true
System.out.println(hashMap.containsKey("brother")); // Output: false
System.out.println(hashMap.get("mother")); // Output: Catelyn Stark
System.out.println(hashMap.getOrDefault("brother", "Bran Stark")); // Output: Bran Stark
//Print all elements of HashMap
System.out.println(hashMap);
//Output:
// {mother=Catelyn Stark, birthPlace=Winterfell, father=Eddard Stark, name=Arya Starc, theme=The Needle}
Code language: Java (java)
Apart from this there are many ways to create a HashMap inline. We recommend you reading How to Initialize a HashMap Inline in Java.
Modify Elements in a HashMap
You have two different methods to modify any existing entry in a HashMap.
- put: The put methods work like upsert. Where an existing entry will be replaced. If the existing entry is not found, a new entry will be created.
- replace: The replace methods, will work if the entry exists already. If the entry doesn’t exist, nothing will happen.
hashMap.put("brother", "Bran Stark");
System.out.println(hashMap.get("brother")); // Output: Bran Stark
// Re-insert to update
hashMap.put("brother", "Rob Stark");
System.out.println(hashMap.get("brother")); // Output: Rob Stark
Code language: Java (java)
HashMaps and Multi Threading.
Working with multi-threading is always tricky. But, when it comes to HashMaps along with multi threading, it is straightforward. However, you need to know few of the basics. Let’s cover those basics here.
HashMaps are not synchronized. This means, you can actually have multiple threads reading and writing from same Hashmap.
For example, consider you have a thread that is iterating a HashMap of size 10. Meanwhile, another thread removes an element from the Hashmap and the size now became 9. This can cause the iteration logic go on toss. To make it easier the iterators are made fail-fast. In other words, when the iterator senses modification of the underlying HashMap, they immedietly throw ConcurrentModificationException.
This behavior is really helpful, as you can rely on the application to fail and hence no need to worry about having dirty reads. Although, if snychronization between threads is really important for you, you can still synchnronize the blocks or the objects accessing the HashMaps.
Alternatevly, you can use a Synchrnoized copy of your Hashmap. Refer to below example to learn how to get a synchronized copy of your HashMap.
Map synchronizedMap = Collections.synchronizedMap(hashMap);
Code language: Java (java)
The synchronizedMap is a synchrnozed copy of your map. You can use this map safely with the threads. However, remember this is a copy of your existing hashmap. Therefore, if you have a really big hashmap this will be expensive on the memory.
Capacity and Load Factors of HashMap
Like any other Java collections, the HapMaps comes with an initial capacity. When you create or use a collection the capacity helps in keeping the memory utilisation optimized. For example, if you want to store just couple of entries into a HashMap, and the the HashMap you create has a capacity of hundreds of entries. This will utilize more memory.
On the other hand if you create an HashMap of capacity 10 and add more elements it will cause re-hashing and affect performance as well as load on garbage collection.
The HashMaps have default initial capacity of 16 and load factor of 0.75. Which means, when a HashMap is 75% occupied, the background process will start finding larger space. Once more space is allocated, all the entries in the HashMap will be migrated to new location. Moreover, it will also re-hash all the keys. Hence it is important to know your needs and create HashMaps of optimal size.
Below are the HashMap constructors which let’s you decide the capacity and load factor.
- new HashMap(): A default constructor which creates an empty HashMap of initial capacity of 16 and load factor of 0.75.
- new HashMap(int initialCapacity): It creates an empty HashMap of the given initial capacity. However, the load factor still defaults to 0.75.
- new HashMap(int initialCapacity, float loadFactor): Creates an empty HashMap of given initial capacity and given load factor.
- How to Initialize a HashMap Inline in Java
- Convert List of Strings to Concatenated String with Delimiter in Java
- Introduction to Java LinkedHashSet With Examples
- Examples of Converting List to Map using Streams
- How to Initialize a HashSet Inline in Java
- Introduction to Java TreeSets With Examples
- Introduction to Java Collections Map Interface
When to use HashMaps
HashMaps have variety of usages. Having a key value structure they can be used to store many different type of elements. They are useful, when you do not have to worry about sorting or ordering.
Consider, you have a properties file to read and keep in memory whenever you application wants to access any property. You can read the file once and store all they key value pairs in a HashMap and keep the map accessible to your code. Then your application can query the map with a specific key and access the associated value in constant amount of time.
Moreover, because of its structure it can be used to hold entire database table in a List<Map<String, Object>>
. Where each map in the list represent entire row of a table. Similarly, it can also used to generically hold entire request body in a web application.
Additionally, in below example we will create an User instance and map it to a HashMap using fasterxml library.
User user = new User(1L, "Arya", "Stark", 14);
ObjectMapper objectMapper = new ObjectMapper();
// Covert object to a Map
Map<String, Object> objectMap = objectMapper.convertValue(user, Map.class);
System.out.println(objectMap);
// Output:
// {id=1, name=Arya, lastName=Stark, age=14}
Code language: Java (java)
Summary
This is an end to a short Introduction to Java Collections HashMap. We have seen basic featues of HashMap and learnt how it can be generically used to hold almost anything.
HashMaps are key value stores which use hash tables to strore the elements. Each HashMap has an initial capacity and a load factor. Exeeding the lower factor, causes the keys to re-hashed into new location.
HashMap’s element specific oeprations are constant in time. Hence HashMaps can also be used as a lookup storage.