Quick Answer

A Java HashMap stores data as key-value pairs, so you can look up a value fast using its key. You create one with generics like Map, add pairs with put, read them with get or getOrDefault, and check keys with containsKey. It is ideal for tasks such as counting words, caching results, or storing settings.

What Is a Java HashMap?

A Java HashMap is a built-in data structure that stores information as key-value pairs. Instead of scanning a list one item at a time, you hand the map a key and it hands back the matching value almost instantly. Think of a class attendance register: the roll number is the key, and the student's name is the value.

HashMap lives in the java.util package and is one of the most used tools in real Java code. In this tutorial you will learn how to create a java hashmap, add and read data, deal with keys that do not exist, loop over everything inside, and finish with a practical word-frequency counter. If you want a full guided path, our free Java course covers this and much more.

Creating a HashMap With Generics

You create a HashMap using generics — the angle brackets that tell Java what type the keys and values are. The first type is the key, the second is the value.

import java.util.HashMap;
import java.util.Map;

// Key = String (name), Value = Integer (age)
Map<String, Integer> ages = new HashMap<>();

Notice we declare the variable as Map but create a HashMap. Coding to the Map interface is a good habit — it keeps your code flexible if you later switch to a different map type. The empty <> on the right is the "diamond operator"; Java infers the types from the left side, so you do not have to repeat them.

One rule to remember: keys and values must be objects, not primitives. Use Integer instead of int, and Double instead of double. Java automatically converts between them (this is called autoboxing), so you can still write plain numbers like 21.

Adding and Reading Values: put and get

Add a pair with put(key, value) and read it back with get(key).

ages.put("Aarav", 21);
ages.put("Diya", 19);
ages.put("Kabir", 24);

int aaravAge = ages.get("Aarav"); // 21
System.out.println(aaravAge);

Two things are worth knowing about put:

  • Keys are unique. Calling put with a key that already exists overwrites the old value instead of adding a second copy. So ages.put("Aarav", 22) simply changes Aarav's age to 22.
  • put returns the old value. It gives back whatever the key held before (or null if it was new). Most of the time you ignore this return value.

The bigger gotcha is get: if the key is missing, it returns null, not zero. Assigning that to an int throws a NullPointerException, because Java cannot unbox null into a primitive. That is exactly the problem the next section solves.

Handling Missing Keys: getOrDefault and containsKey

Real data is messy, so you constantly deal with keys that may not exist. Java gives you two clean tools for this.

getOrDefault

getOrDefault(key, fallback) returns the value if the key is present, or the fallback you supply if it is not. No null, no crash.

int score = ages.getOrDefault("Meera", 0); // Meera not in map -> 0
System.out.println(score); // 0

containsKey

When you only want to check whether a key exists (not read its value), use containsKey, which returns a boolean.

if (ages.containsKey("Diya")) {
    System.out.println("Diya is " + ages.get("Diya"));
} else {
    System.out.println("No record for Diya");
}

Recommendation: reach for getOrDefault when you want a safe value, and containsKey when you want a yes/no answer. Both are far safer than calling get and hoping the key is there.

Removing and Updating Entries

Remove a pair with remove(key). It deletes the entry and returns the value that was stored (or null if the key was not present).

ages.put("Rohan", 30);
Integer removed = ages.remove("Rohan"); // removed = 30
boolean stillThere = ages.containsKey("Rohan"); // false

Removing a key that does not exist is harmless — it just returns null and changes nothing.

To update a value, you can simply put again, but two helpers make common patterns cleaner:

  • putIfAbsent(key, value) — sets the value only if the key is not already present.
  • merge(key, value, function) — combines the new value with any existing one. This is great for counters, as you will see below.
ages.putIfAbsent("Diya", 99); // Diya already exists, so this is ignored
System.out.println(ages.get("Diya")); // still 19

Looping Over a HashMap: entrySet and keySet

Sooner or later you need to loop over everything in the map. There are two common ways.

entrySet — keys and values together

This is the most efficient option because you get each key and its value in a single step.

for (Map.Entry<String, Integer> entry : ages.entrySet()) {
    System.out.println(entry.getKey() + " -> " + entry.getValue());
}

keySet — keys only

Use this when you only care about the keys, or want to look values up as you go.

for (String name : ages.keySet()) {
    System.out.println(name);
}

There is also values() if you need only the values. One important warning: a HashMap does not keep any order. The pairs may print in a different order than you inserted them, and that order can even change between runs. If you need insertion order, use LinkedHashMap; if you need keys sorted, use TreeMap.

Example: A Word-Frequency Counter

Let us tie it all together with a classic task: counting how often each word appears in a sentence. This one example uses split, getOrDefault, put, and entrySet.

import java.util.HashMap;
import java.util.Map;

public class WordCount {
    public static void main(String[] args) {
        String text = "the cat sat on the mat the cat ran";
        String[] words = text.split(" ");

        Map<String, Integer> counts = new HashMap<>();
        for (String word : words) {
            // read current count (0 if new), add 1, store it back
            counts.put(word, counts.getOrDefault(word, 0) + 1);
        }

        for (Map.Entry<String, Integer> entry : counts.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
    }
}

Sample output (order may vary):

ran: 1
sat: 1
cat: 2
on: 1
the: 3
mat: 1

The heart of it is one line: counts.getOrDefault(word, 0) + 1. For a brand-new word it reads 0 and stores 1; for a repeat it reads the old count and adds one. You can even shorten the whole loop body to counts.merge(word, 1, Integer::sum); — it does exactly the same thing.

Gotchas and When to Use HashMap

HashMap is the right default map for most jobs, but keep these points in mind.

  • No order. Never rely on the order of items in a HashMap.
  • Not thread-safe. If several threads write to the same map at once, use ConcurrentHashMap instead.
  • Nulls. HashMap allows one null key and any number of null values, but leaning on nulls invites bugs — prefer getOrDefault.
  • Custom key objects. If your key is your own class, override both equals and hashCode, or lookups will silently fail. String and Integer already do this correctly.
  • Do not edit while iterating. Adding or removing keys during a normal for-each loop throws a ConcurrentModificationException; use the iterator's own remove method instead.

Here is how the common map types compare:

FeatureHashMapLinkedHashMapTreeMap
Fast get/putYesYesSlower
Keeps insertion orderNoYesNo
Keys kept sortedNoNoYes
Allows null keyYesYesNo

For a deeper, hands-on path through collections and the rest of the language, work through our free Java course.

Frequently Asked Questions

What is the difference between a HashMap and a HashSet?

A HashMap stores key-value pairs, while a HashSet stores only single values with no attached value. In fact, a HashSet is backed by a HashMap internally. Use a HashMap when each item needs an associated value (like a name to an age), and a HashSet when you only need to track uniqueness.

Can a Java HashMap have duplicate keys?

No. Keys must be unique. If you call put with a key that already exists, the new value simply replaces the old one instead of creating a second entry. Values, on the other hand, can be duplicated as much as you like.

Why does get() return null instead of throwing an error for a missing key?

HashMap treats a missing key as "no value", which it represents with null. It does not throw an exception, so you can decide how to handle the absence yourself. To stay safe, use getOrDefault to supply a fallback value, or containsKey to check first, and avoid a NullPointerException from unboxing null into a primitive.

Is a HashMap ordered?

No. A HashMap gives no guarantee about the order of its entries, and that order can change between runs. If you need to preserve insertion order, use LinkedHashMap. If you need the keys kept in sorted order, use TreeMap.

Is HashMap thread-safe?

No. A plain HashMap is not safe for multiple threads writing at the same time. For concurrent access, use ConcurrentHashMap, which is built for that purpose and performs much better than older synchronized wrappers.