Java HashMap Tutorial with Examples: put, get, Iterate, Count, and Sort by Value

Almost every real program needs to look something up quickly: a student's grade by ID number, a product's price by its code, or how many times a word appears in a text. In Java, the go-to tool for this job is the HashMap. It stores data as key-value pairs and can find a value by its key almost instantly, no matter how many entries it holds.

In this tutorial, you will learn how to use HashMap in Java step by step: creating a map, adding and reading values, the different ways to loop through it, counting with merge(), grouping with computeIfAbsent(), sorting a HashMap by value, and using your own class as a key. Every example is a complete program that was compiled and run on Java 21, and the output shown is the real output.

What Is a HashMap?

A HashMap<K, V> is part of the Java Collections Framework (java.util). Think of it as a dictionary: each key is unique and points to exactly one value. A few important rules:

  • Keys are unique. Putting a value with an existing key replaces the old value.
  • Order is not guaranteed. A HashMap does not remember insertion order or sort its keys.
  • One null key and many null values are allowed.
  • It is fast. get(), put() and remove() run in constant time on average.
  • It is not thread-safe. Use ConcurrentHashMap when multiple threads write to the same map.

Requirements

  • JDK 11 or newer (the examples were tested with JDK 21).
  • Any editor or IDE: VS Code, IntelliJ IDEA, NetBeans or Eclipse.

Step 1: Basic HashMap Operations

Let's start with the methods you will use every day: put(), get(), getOrDefault(), containsKey(), containsValue(), remove() and size(). Create a file named HashMapBasics.java:

  1. import java.util.HashMap;
  2. import java.util.Map;
  3.  
  4. public class HashMapBasics {
  5.     public static void main(String[] args) {
  6.         // Create a HashMap: key = student name, value = score
  7.         Map<String, Integer> scores = new HashMap<>();
  8.  
  9.         // put() adds a new entry or replaces the value of an existing key
  10.         scores.put("Ana", 88);
  11.         scores.put("Ben", 75);
  12.         scores.put("Carla", 92);
  13.         scores.put("Ben", 81); // replaces 75
  14.  
  15.         System.out.println("Map: " + scores);
  16.         System.out.println("Size: " + scores.size());
  17.  
  18.         // get() returns the value, or null if the key is missing
  19.         System.out.println("Ana's score: " + scores.get("Ana"));
  20.         System.out.println("Dan's score: " + scores.get("Dan"));
  21.  
  22.         // getOrDefault() avoids null
  23.         System.out.println("Dan's score (default): " + scores.getOrDefault("Dan", 0));
  24.  
  25.         // containsKey() and containsValue()
  26.         System.out.println("Has Carla? " + scores.containsKey("Carla"));
  27.         System.out.println("Anyone with 100? " + scores.containsValue(100));
  28.  
  29.         // remove() deletes by key and returns the old value
  30.         Integer removed = scores.remove("Ben");
  31.         System.out.println("Removed Ben (" + removed + "), map is now: " + scores);
  32.     }
  33. }

Run it with java HashMapBasics.java (Java 11 or newer can run a single source file directly). Output:

Map: {Carla=92, Ana=88, Ben=81}
Size: 3
Ana's score: 88
Dan's score: null
Dan's score (default): 0
Has Carla? true
Anyone with 100? false
Removed Ben (81), map is now: {Carla=92, Ana=88}

Notice three things in the output. First, putting "Ben" a second time replaced his score instead of adding a new entry. Second, get("Dan") returned null because the key does not exist; calling a method on that result would throw a NullPointerException, which is why getOrDefault() is safer. Third, the map printed the names in an order that is different from how we inserted them. That is normal for a HashMap.

We declared the variable as Map<String, Integer> instead of HashMap<String, Integer>. Coding to the interface is a good habit, because you can later switch to a TreeMap or LinkedHashMap by changing only one line.

Step 2: How to Iterate Over a HashMap

There are three common ways to loop through a HashMap. Use entrySet() when you need both the key and the value, keySet() or values() when you need only one side, and forEach() with a lambda for short, readable code. Save this as IterateHashMap.java:

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import java.util.TreeMap;
  4.  
  5. public class IterateHashMap {
  6.     public static void main(String[] args) {
  7.         Map<String, Double> prices = new HashMap<>();
  8.         prices.put("Coffee", 120.0);
  9.         prices.put("Tea", 95.5);
  10.         prices.put("Cake", 150.0);
  11.  
  12.         // 1. entrySet(): best when you need both key and value
  13.         System.out.println("-- entrySet --");
  14.         for (Map.Entry<String, Double> entry : prices.entrySet()) {
  15.             System.out.println(entry.getKey() + " = " + entry.getValue());
  16.         }
  17.  
  18.         // 2. keySet() and values()
  19.         System.out.println("-- keySet --");
  20.         for (String item : prices.keySet()) {
  21.             System.out.println(item);
  22.         }
  23.         double total = 0;
  24.         for (double p : prices.values()) {
  25.             total += p;
  26.         }
  27.         System.out.println("Total of all prices: " + total);
  28.  
  29.         // 3. forEach() with a lambda (Java 8+)
  30.         System.out.println("-- forEach --");
  31.         prices.forEach((item, price) -> System.out.printf("%-6s PHP %.2f%n", item, price));
  32.  
  33.         // HashMap does not keep any order. Use TreeMap for sorted keys.
  34.         System.out.println("-- sorted by key --");
  35.         new TreeMap<>(prices).forEach((item, price) -> System.out.println(item + " -> " + price));
  36.     }
  37. }

Run it with java IterateHashMap.java (Java 11 or newer can run a single source file directly). Output:

-- entrySet --
Tea = 95.5
Cake = 150.0
Coffee = 120.0
-- keySet --
Tea
Cake
Coffee
Total of all prices: 365.5
-- forEach --
Tea    PHP 95.50
Cake   PHP 150.00
Coffee PHP 120.00
-- sorted by key --
Cake -> 150.0
Coffee -> 120.0
Tea -> 95.5

If you need the keys in alphabetical order, copy the map into a TreeMap, as shown at the end of the program. If you need to keep the order in which items were added, use a LinkedHashMap instead.

Step 3: Counting, Grouping and Sorting by Value

Since Java 8, Map has methods that remove a lot of boilerplate code. The most useful are:

  • merge(key, value, function) – perfect for counters. If the key is new, it stores the value; otherwise it combines the old and new values.
  • computeIfAbsent(key, function) – creates a value only when the key is missing. Great for grouping items into lists.
  • putIfAbsent(key, value) – adds the entry only if the key does not exist yet.

This program counts words in a sentence, prints the top 3 words sorted by count, and groups words by their first letter. Save it as WordCounter.java:

  1. import java.util.ArrayList;
  2. import java.util.HashMap;
  3. import java.util.List;
  4. import java.util.Map;
  5.  
  6. public class WordCounter {
  7.     public static void main(String[] args) {
  8.         String text = "the quick brown fox jumps over the lazy dog the fox runs";
  9.  
  10.         // Count words with merge(): add 1, or start at 1 if the key is new
  11.         Map<String, Integer> counts = new HashMap<>();
  12.         for (String word : text.split("\\s+")) {
  13.             counts.merge(word, 1, Integer::sum);
  14.         }
  15.         System.out.println("the = " + counts.get("the"));
  16.         System.out.println("fox = " + counts.get("fox"));
  17.         System.out.println("dog = " + counts.get("dog"));
  18.  
  19.         // Top 3 words, highest count first, then alphabetical
  20.         System.out.println("-- top 3 --");
  21.         counts.entrySet().stream()
  22.               .sorted(Map.Entry.<String, Integer>comparingByValue().reversed()
  23.                       .thenComparing(Map.Entry.comparingByKey()))
  24.               .limit(3)
  25.               .forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
  26.  
  27.         // Group words by their first letter with computeIfAbsent()
  28.         Map<Character, List<String>> byLetter = new HashMap<>();
  29.         for (String word : counts.keySet()) {
  30.             byLetter.computeIfAbsent(word.charAt(0), k -> new ArrayList<>()).add(word);
  31.         }
  32.         byLetter.values().forEach(list -> list.sort(null));
  33.         System.out.println("Words starting with 'f': " + byLetter.get('f'));
  34.         System.out.println("Words starting with 'd': " + byLetter.get('d'));
  35.  
  36.         // putIfAbsent() only adds when the key is missing
  37.         counts.putIfAbsent("cat", 0);
  38.         counts.putIfAbsent("the", 100); // ignored, "the" already exists
  39.         System.out.println("cat = " + counts.get("cat") + ", the = " + counts.get("the"));
  40.     }
  41. }

Run it with java WordCounter.java (Java 11 or newer can run a single source file directly). Output:

the = 3
fox = 2
dog = 1
-- top 3 --
the: 3
fox: 2
brown: 1
Words starting with 'f': [fox]
Words starting with 'd': [dog]
cat = 0, the = 3

The line counts.merge(word, 1, Integer::sum) replaces the classic five-line "if the key exists, add one, else put one" pattern. To sort by value, we stream the entries and use Map.Entry.comparingByValue().reversed(), then break ties alphabetically with comparingByKey(). A HashMap itself can never be sorted, so sorting always produces a new list or stream.

Step 4: Using Your Own Class as a Key

You can use any object as a key, but there is one rule you must follow: override both equals() and hashCode(). HashMap uses hashCode() to find the right bucket and equals() to find the exact key inside it. If you forget them, two objects with the same data are treated as different keys, and get() returns null even though the entry is "there." Save this as Student.java:

  1. import java.util.HashMap;
  2. import java.util.Map;
  3. import java.util.Objects;
  4.  
  5. public class Student {
  6.     private final String id;
  7.     private final String name;
  8.  
  9.     public Student(String id, String name) {
  10.         this.id = id;
  11.         this.name = name;
  12.     }
  13.  
  14.     // Two students are equal when their IDs are equal
  15.     @Override
  16.     public boolean equals(Object o) {
  17.         if (this == o) return true;
  18.         if (!(o instanceof Student)) return false;
  19.         return id.equals(((Student) o).id);
  20.     }
  21.  
  22.     @Override
  23.     public int hashCode() {
  24.         return Objects.hash(id);
  25.     }
  26.  
  27.     @Override
  28.     public String toString() {
  29.         return name + " (" + id + ")";
  30.     }
  31.  
  32.     public static void main(String[] args) {
  33.         Map<Student, String> sections = new HashMap<>();
  34.         sections.put(new Student("2026-001", "Ana Cruz"), "BSIT 2A");
  35.  
  36.         // A different object with the same ID finds the same entry
  37.         Student lookup = new Student("2026-001", "Ana Cruz");
  38.         System.out.println("Section of " + lookup + ": " + sections.get(lookup));
  39.         System.out.println("Entries: " + sections.size());
  40.     }
  41. }

Run it with java Student.java (Java 11 or newer can run a single source file directly). Output:

Section of Ana Cruz (2026-001): BSIT 2A
Entries: 1

Because both methods are based on the student ID, a brand-new Student object with the same ID finds the existing entry. Try deleting the two methods and running the program again: the lookup will print null. Also, avoid changing a key object's fields after you put it in a map, since its hash code would change and the entry would become unreachable. Making key fields final, as we did here, prevents that problem.

HashMap vs LinkedHashMap vs TreeMap

  • HashMap – fastest, no ordering. Use it by default.
  • LinkedHashMap – keeps insertion order, slightly more memory. Good for menus or recently used items.
  • TreeMap – keeps keys sorted, operations are O(log n). Good for reports sorted by name or date.

Common HashMap Mistakes

  • Assuming order. Never rely on the print order of a HashMap; use LinkedHashMap or TreeMap when order matters.
  • Unboxing a null. int score = scores.get("Dan"); throws a NullPointerException when the key is missing. Use getOrDefault().
  • Modifying the map while looping. Calling map.remove() inside a for-each loop can throw a ConcurrentModificationException. Use map.entrySet().removeIf(...) instead.
  • Custom keys without equals() and hashCode(). Lookups silently fail, as explained in Step 4.

Conclusion

HashMap is one of the most useful classes in Java. With just put(), get() and getOrDefault() you can already solve many problems, and the newer methods like merge() and computeIfAbsent() make counting and grouping short and readable. Practice by building a small phonebook, an inventory counter, or a grade lookup using the examples above. You can download all four example programs below.

Happy coding!