HashMap in Java is a collection that stores data as key-value pairs. Each key identifies a value, allowing applications to retrieve information efficiently without searching through every element. HashMap is part of the Java Collections Framework and is commonly used for lookups, caching, configuration data, grouping, and frequency counting.
Unlike an ArrayList, which is primarily accessed using numeric indexes, a HashMap is accessed using keys. For example, an application can associate a username with a score, a product ID with a product, or a country code with a country name.
What Is HashMap in Java?
HashMap<K, V> stores mappings between a key type
K and a value type V.
Keys must be unique. If the same key is inserted again, its associated value is replaced.
import java.util.HashMap;
import java.util.Map;
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
scores.put("Charlie", 95);
System.out.println(scores);
In this example, the names are keys and the scores are values. The map can retrieve a score directly by supplying the corresponding name.
How HashMap Stores Data
HashMap uses a hashing-based structure to organize key-value entries. When a key is added, Java uses its hash information to determine where the entry should be stored internally.
When a value is requested using a key, the hash information helps locate the corresponding entry without scanning every key in the map.
Hash collisions can occur when different keys map to the same internal location.
Java's HashMap implementation handles collisions internally, so developers normally work
with the collection through methods such as put(),
get(), and remove()
rather than managing buckets directly.
Creating a HashMap
A HashMap is commonly declared using the Map interface
and initialized with a HashMap implementation.
import java.util.HashMap;
import java.util.Map;
Map<Integer, String> users = new HashMap<>();
users.put(101, "Alice");
users.put(102, "Bob");
users.put(103, "Charlie");
Using Map as the variable type makes the code depend on
the interface. This can make it easier to replace the implementation when requirements change.
Adding Key-Value Pairs
The put() method adds a key-value pair to the map.
Map<String, String> countries = new HashMap<>();
countries.put("IN", "India");
countries.put("US", "United States");
countries.put("JP", "Japan");
System.out.println(countries);
If a key already exists, calling put() with that key
replaces its existing value.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 80);
scores.put("Alice", 95);
System.out.println(scores.get("Alice"));
The final value associated with Alice is
95.
Getting a Value from HashMap
Use the get() method to retrieve a value using its key.
Map<Integer, String> users = new HashMap<>();
users.put(101, "Alice");
users.put(102, "Bob");
String name = users.get(101);
System.out.println(name);
If the specified key does not exist, get() returns
null.
Using getOrDefault()
The getOrDefault() method provides a fallback value
when a key is not present.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
int aliceScore = scores.getOrDefault("Alice", 0);
int bobScore = scores.getOrDefault("Bob", 0);
System.out.println(aliceScore);
System.out.println(bobScore);
This method is particularly useful in counting and aggregation problems because it avoids separate existence checks for every key.
Updating a HashMap Value
A value can be updated simply by calling put() again
with the existing key.
Map<String, Integer> inventory = new HashMap<>();
inventory.put("Keyboard", 10);
inventory.put("Mouse", 15);
inventory.put("Keyboard", 25);
System.out.println(inventory);
The key remains the same, while its associated value changes from
10 to 25.
Checking Whether a Key Exists
Use containsKey() when you need to determine whether
a particular key exists in the map.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
if (scores.containsKey("Alice")) {
System.out.println("Alice exists");
}
Checking Whether a Value Exists
The containsValue() method checks whether at least one
entry contains the specified value.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
System.out.println(scores.containsValue(90));
System.out.println(scores.containsValue(100));
Unlike key lookup, searching for a value may require checking multiple entries,
so containsValue() is generally O(n).
Removing Entries
Use remove() to delete an entry using its key.
Map<Integer, String> users = new HashMap<>();
users.put(101, "Alice");
users.put(102, "Bob");
users.put(103, "Charlie");
users.remove(102);
System.out.println(users);
Java also provides an overload that removes an entry only when both the key and value match.
users.remove(103, "Charlie");
Iterating Through a HashMap
HashMap provides several ways to iterate over its contents. A common approach is using
entrySet(), which gives access to both keys and values.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
scores.put("Charlie", 95);
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(
entry.getKey() + " = " + entry.getValue()
);
}
Iterating Over Keys
for (String name : scores.keySet()) {
System.out.println(name);
}
Iterating Over Values
for (Integer score : scores.values()) {
System.out.println(score);
}
HashMap does not guarantee a predictable iteration order. If a specific ordering requirement
exists, another map implementation such as LinkedHashMap
or TreeMap may be more appropriate.
HashMap Size and Empty Check
Use size() to find the number of key-value mappings and
isEmpty() to check whether the map contains no entries.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
System.out.println(scores.size());
System.out.println(scores.isEmpty());
Clearing a HashMap
The clear() method removes all mappings from the HashMap.
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 90);
scores.put("Bob", 85);
scores.clear();
System.out.println(scores);
System.out.println(scores.isEmpty());
HashMap and Null Values
A HashMap permits a single null key and can contain
multiple entries whose values are null.
Map<String, String> users = new HashMap<>();
users.put(null, "Unknown");
users.put("Alice", null);
System.out.println(users.get(null));
System.out.println(users.get("Alice"));
Because get() can return null both when a key is absent
and when a key is explicitly mapped to null, use containsKey()
when that distinction matters.
Frequency Counting with HashMap
One of the most common uses of HashMap in coding problems is counting how frequently each value occurs.
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
int[] numbers = {2, 3, 2, 5, 3, 2};
Map<Integer, Integer> frequency = new HashMap<>();
for (int number : numbers) {
frequency.put(
number,
frequency.getOrDefault(number, 0) + 1
);
}
System.out.println(frequency);
}
}
This pattern can be adapted for characters, words, IDs, categories, and many other situations where occurrences need to be counted.
Using compute() and merge()
HashMap also provides methods that make conditional updates more concise.
For example, merge() can be used for frequency counting.
Map<String, Integer> frequency = new HashMap<>();
String[] words = {"java", "python", "java", "csharp"};
for (String word : words) {
frequency.merge(word, 1, Integer::sum);
}
System.out.println(frequency);
These methods are useful when the update logic depends on whether a key already exists.
HashMap with Custom Objects
HashMap can use custom objects as keys or values. When custom objects are used as keys,
correct implementations of equals() and
hashCode() are important because HashMap relies on
these methods to identify equivalent keys.
import java.util.HashMap;
import java.util.Map;
class Product {
String name;
Product(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Map<Integer, Product> products = new HashMap<>();
products.put(101, new Product("Keyboard"));
products.put(102, new Product("Mouse"));
Product product = products.get(101);
System.out.println(product.name);
}
}
Using simple immutable key types such as strings, numbers, or well-defined value objects can make key behavior easier to reason about.
HashMap Time Complexity
HashMap operations are generally efficient because hashing allows Java to locate entries without scanning the entire collection in the normal case.
| Operation | Typical Complexity |
|---|---|
| put() | O(1) average |
| get() | O(1) average |
| containsKey() | O(1) average |
| remove() | O(1) average |
| containsValue() | O(n) |
| Iteration | O(n) |
The average O(1) complexity assumes well-distributed hashes and normal operation. HashMap performance can be affected by collisions, resizing, and the quality of key implementations.
HashMap vs ArrayList
ArrayList and HashMap solve different lookup problems. The choice depends on whether data is naturally accessed by position or by a key.
| Feature | ArrayList | HashMap |
|---|---|---|
| Data model | Ordered elements | Key-value mappings |
| Access | By index | By key |
| Duplicate values | Allowed | Allowed |
| Unique keys | Not applicable | Required |
HashMap vs TreeMap
Both classes implement the Map interface, but they
provide different ordering and performance characteristics.
| Feature | HashMap | TreeMap |
|---|---|---|
| Ordering | No guaranteed iteration order | Sorted by keys |
| get() | O(1) average | O(log n) |
| put() | O(1) average | O(log n) |
| Main purpose | Fast key lookup | Sorted key operations |
Common HashMap Mistakes
- Assuming HashMap preserves insertion order.
- Using a HashMap when sorted key order is a requirement.
- Expecting
get()to distinguish a missing key from a key mapped to null. - Using mutable objects as keys without carefully handling
equals()andhashCode(). - Assuming
containsValue()is as efficient as key lookup. - Forgetting that keys are unique and inserting the same key replaces its previous value.
- Using HashMap when a Set is sufficient for simple membership checking.
- Ignoring thread-safety requirements when a map is accessed concurrently.
When Should You Use HashMap?
HashMap is appropriate when data naturally forms a relationship between a unique key and an associated value.
- You need fast average-case lookup by key.
- Each record has a unique identifier.
- Ordering is not required.
- You need to count frequencies or group values.
- You need an in-memory lookup or cache.
- You need to associate IDs with application objects.
Practical Example: User Lookup
A backend application might store users by their unique ID so that a user can be retrieved without scanning an entire list.
import java.util.HashMap;
import java.util.Map;
class User {
int id;
String name;
User(int id, String name) {
this.id = id;
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Map<Integer, User> users = new HashMap<>();
users.put(101, new User(101, "Alice"));
users.put(102, new User(102, "Bob"));
users.put(103, new User(103, "Charlie"));
User user = users.get(102);
if (user != null) {
System.out.println("User: " + user.name);
}
}
}
Key Takeaways
HashMapstores data as key-value pairs.- Keys are unique, while values can be duplicated.
put()adds or updates a mapping.get()retrieves a value using its key.containsKey()checks whether a key exists.remove()deletes a mapping.- HashMap provides O(1) average-case lookup, insertion, and removal under typical conditions.
- HashMap does not guarantee a predictable iteration order.
- HashMap is widely useful for frequency counting, caching, grouping, and key-based lookups.