Key Takeaways
- Use Map<String, Integer> for a map with String keys and Integer values, backed by a HashMap.
- put() adds or replaces a mapping; get() retrieves a value; remove() deletes a mapping; clear() removes all mappings.
- Iterate with entrySet() when you need both keys and values. HashMap does not guarantee iteration order.
- HashMap permits one null key and null values, but it isn't synchronized. Use a suitable concurrent map for shared mutable data.
A Java HashMap stores key-value pairs so you can retrieve a value by its key. These Java HashMap examples show how to create, populate, query, remove, clear, and iterate over a map in one runnable program, followed by practical update patterns.
The HashMap API documentation defines its behavior; the important starting point is that keys are unique, while values can repeat.
Runnable Java HashMap Examples
Save this program as HashMapExamples.java. It includes the imports and adds entries before iterating, so you don't need to assemble disconnected snippets.
import java.util.HashMap;
import java.util.Map;
public class HashMapExamples {
public static void main(String[] args) {
Map<String, Integer> ageMap = new HashMap<>();
// Add entries, then replace Sam's value.
ageMap.put("Sam", 40);
ageMap.put("Ana", 28);
Integer previousAge = ageMap.put("Sam", 41);
System.out.println("Previous age: " + previousAge);
// Retrieve values and check whether a key exists.
System.out.println("Sam's age: " + ageMap.get("Sam"));
System.out.println("Contains Sam: " + ageMap.containsKey("Sam"));
System.out.println("Lee's age or default: "
+ ageMap.getOrDefault("Lee", 0));
// Remove one entry and add another.
Integer removedAge = ageMap.remove("Ana");
System.out.println("Removed age: " + removedAge);
ageMap.put("Lee", 34);
// Visit every remaining key-value pair.
for (Map.Entry<String, Integer> entry : ageMap.entrySet()) {
System.out.println("Key: " + entry.getKey()
+ ", Value: " + entry.getValue());
}
// Remove all entries after iteration.
ageMap.clear();
System.out.println("Empty: " + ageMap.isEmpty());
}
}With a current JDK installed, run it using Java's source-file launcher:
java HashMapExamples.javaCreate a HashMap
In Map<String, Integer>, the first type is the key type and the second is the value type. Here, names are String keys and ages are Integer values. Integer is the object wrapper for int; Java boxes the numeric arguments automatically.
The variable uses the Map interface because these operations don't need HashMap-specific methods. This keeps the implementation choice separate from how you use the map. For the wider context, see our guide to the Java Collections Framework.
The diamond operator, <>, lets the compiler infer the constructor's type arguments instead of repeating String, Integer. It reduces duplication without removing type checking; see the official explanation of generic type inference.
Add and Replace an Entry
ageMap.put("Sam", 40) adds a mapping. Calling put("Sam", 41) replaces its value rather than adding a second Sam entry, and returns the previous value, 40. For a previously absent key, put() returns null; it also returns null when the previous mapped value was null.
Get a Value and Supply a Default
get("Sam") retrieves Sam's current age, while containsKey("Sam") checks whether that key has a mapping. getOrDefault("Lee", 0) returns 0 because Lee is absent at that point; it does not insert Lee into the map. These operations are part of the Map interface.
Remove an Entry
remove("Ana") deletes Ana's mapping and returns its previous value, 28. A null return means either the key was absent or its previous value was null.
Loop Through Keys and Values
entrySet() provides a set view of the map's mappings. Each Map.Entry exposes its key through getKey() and its value through getValue(), avoiding a separate get() lookup for every key.
The loop prints Sam and Lee, but their relative order is unspecified. If you only need keys, use keySet(); if you only need values, use values().
Clear a HashMap
clear() removes every mapping, so isEmpty() returns true afterward. The map object remains available for reuse, but clearing it isn't a promise to shrink its backing storage. Reuse or replacement should follow your application's lifecycle and memory needs.
Null Values and Safe Lookups
HashMap permits one null key and multiple null values. Consequently, get() returning null doesn't tell you whether the key is absent or explicitly mapped to null; containsKey() distinguishes those cases.
Add this fragment after clear() inside the previous program's main method:
// Fragment: place inside main in HashMapExamples.java.
ageMap.put(null, 99);
ageMap.put("Unknown", null);
System.out.println(ageMap.get("Unknown")); // null
System.out.println(ageMap.containsKey("Unknown")); // true
System.out.println(ageMap.getOrDefault("Unknown", 0)); // null
System.out.println(ageMap.getOrDefault("Missing", 0)); // 0Update Values with putIfAbsent, computeIfAbsent, and merge
Use these methods to express common updates directly:
putIfAbsent(key, value)sets a value when the key is absent or currently mapped to null; it preserves an existing non-null value.computeIfAbsent(key, function)calls the function when the key is absent or mapped to null, and stores its result if that result is non-null.merge(key, value, function)stores the supplied non-null value when the key is absent or mapped to null; otherwise, it combines the existing and supplied values. A null result from the combining function removes the mapping.
This program sets a default age, counts word occurrences, and groups words by length. Save it as HashMapUpdates.java and run java HashMapUpdates.java.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HashMapUpdates {
public static void main(String[] args) {
Map<String, Integer> ages = new HashMap<>();
ages.putIfAbsent("Sam", 40);
ages.putIfAbsent("Sam", 99);
System.out.println(ages.get("Sam")); // 40
Map<String, Integer> counts = new HashMap<>();
Map<Integer, List<String>> wordsByLength = new HashMap<>();
String[] words = {"java", "map", "java"};
for (String word : words) {
counts.merge(word, 1, Integer::sum);
wordsByLength.computeIfAbsent(
word.length(), length -> new ArrayList<>()
).add(word);
}
System.out.println(counts.get("java")); // 2
System.out.println(wordsByLength.get(4)); // [java, java]
}
}Integer::sum adds the existing count and the incoming 1. The computeIfAbsent() call creates a list only when needed, then returns the existing or newly created list so add() can append the word. The official guide to map updates with lambda expressions explains both patterns.
Choose the Right Map for Ordering
Don't rely on the order you happen to observe in a HashMap, even if it looks stable in your tests.
| Implementation | Iteration order | Choose it when |
|---|---|---|
| HashMap | No guaranteed order | You need key-based storage without ordering requirements. |
| LinkedHashMap | Insertion order by default | You need entries in insertion order. |
| TreeMap | Keys sorted by natural ordering or a supplied Comparator | You need sorted keys. |
HashMap Performance, Capacity, and Collisions
get() and put() have expected constant-time performance when hashes distribute keys well, not a universal worst-case guarantee. Capacity is the number of buckets; the load factor controls how full the table can become before growth. The default load factor is 0.75.
When you know the expected entry count, Java 19 and later provide HashMap.newHashMap(int numMappings):
// Fragment: place inside main; HashMap and Map are already imported.
Map<String, Integer> expectedAges = HashMap.newHashMap(100);Unlike a constructor's initial-capacity argument, this argument represents the expected number of mappings. Don't oversize indiscriminately: HashMap iteration takes time proportional to capacity plus entry count.
How HashMap Handles Collisions
A collision occurs when different keys land in the same bucket. The OpenJDK HashMap implementation uses linked entries for ordinary buckets and can convert sufficiently crowded buckets into tree bins when the table is large enough; smaller tables are resized instead. These conversion rules are implementation details, not HashMap API guarantees.
You don't need to tune treeification thresholds to use a map. For a deeper explanation, read our Java HashMap implementation guide.
Thread Safety: When to Use ConcurrentHashMap
HashMap isn't synchronized. Don't share it between readers and writers, or multiple writers, without appropriate synchronization. For concurrent access, consider ConcurrentHashMap, which provides thread-safe operations but rejects null keys and null values.
// Fragment: place inside main; fully qualified names avoid another import.
java.util.concurrent.ConcurrentHashMap<String, Integer> sharedCounts =
new java.util.concurrent.ConcurrentHashMap<>();
sharedCounts.merge("java", 1, Integer::sum);ConcurrentHashMap.merge() performs this update atomically. A separate get() followed by put() isn't an atomic read-modify-write operation, even when both calls use a concurrent map.
Common Mistakes
- Changing keys after insertion: don't mutate fields that affect a key's equals() or hashCode() while it's stored in the map.
- Implementing inconsistent key equality: equal keys must have equal hash codes. Review equals() and hashCode() in Java before using custom key classes.
- Removing entries directly during an enhanced for loop: structural changes can trigger ConcurrentModificationException. To remove entries during traversal, use the iterator's own remove() method.
FAQ
How do you create a HashMap in Java?
Declare Map<String, Integer> ageMap = new HashMap<>(); after importing java.util.Map and java.util.HashMap. String is the key type, and Integer is the value type.
How do you iterate over a HashMap's keys and values?
Use an enhanced for loop over entrySet(), with each element declared as Map.Entry matching your key and value types. Call getKey() and getValue() on each entry; HashMap doesn't guarantee their iteration order.
Does HashMap preserve insertion order?
No. HashMap doesn't guarantee insertion order or sorted order. Use LinkedHashMap for insertion order by default, or TreeMap to sort entries by key.
Can a Java HashMap contain null keys or values?
Yes. HashMap permits one null key and multiple null values. Use containsKey() to distinguish an absent key from a key mapped to null, because get() returns null in both cases.
Is HashMap thread-safe?
No. HashMap isn't synchronized, so shared reads and writes require appropriate synchronization. ConcurrentHashMap supports concurrent operations but doesn't permit null keys or null values.
Further Reading
- HashMap API reference — null handling, performance, capacity, and method behavior.
- Map interface reference — key-value contracts and update operations.
- Generic type inference — how the diamond operator avoids repeated type arguments.
- Handling map values with lambda expressions — computeIfAbsent(), merge(), and related patterns.
- LinkedHashMap API reference — insertion-order and access-order behavior.
- TreeMap API reference — sorted keys and comparator requirements.
- ConcurrentHashMap API reference — concurrency guarantees and null restrictions.
- OpenJDK HashMap source — concrete bucket and tree-bin implementation details.
