Count Character Frequency Using HashMap in Java
Problem
A HashMap's key-value structure makes it a natural fit for tallying — the key is the thing being counted and the value is a running total, a pattern that works for characters, words, or any other repeated item.
Given a string, tally how many times each character appears using a HashMap.
Java Program
import java.util.LinkedHashMap;
import java.util.Map;
public class CharacterTallyHashMap {
public static void main(String[] args) {
String text = "banana";
Map<Character, Integer> tally = new LinkedHashMap<>();
for (char c : text.toCharArray()) {
tally.put(c, tally.getOrDefault(c, 0) + 1); // read-or-default, then store the incremented count
}
for (Map.Entry<Character, Integer> entry : tally.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}Output
Core Logic
Walking the string once and bumping each character's running total in a map turns counting into a single pass, with the map itself doing the bookkeeping.
- 1A
LinkedHashMap<Character, Integer>namedtallykeeps each character's count, in the order each one was first seen. - 2
tally.getOrDefault(c, 0)reads a character's running count, defaulting to0the first time it's encountered. - 3
tally.put(c, ...)stores that count back in, incremented by one, in the same line that read it. - 4Once every character has been visited,
entrySet()is walked to print each character next to its final tally.
"banana", 'b' is seen once, 'a' three times, and 'n' twice — printed in the order each character first appeared: b, a, n.Key Point: This exact pattern — read-or-default, then put back incremented — is the general-purpose way to tally anything with a HashMap, not just characters; the same three lines would count words, digits, or any other repeated key.
Why: Every character is visited once to update its tally, and the map holds one entry per distinct character, up to n in the worst case.
Key Concepts
Approach 2: Java 8
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
public class CharacterTallyStream {
public static void main(String[] args) {
String text = "banana";
// Groups equal characters together and counts how many are in each group
Map<Character, Long> tally = text.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(c -> c, LinkedHashMap::new, Collectors.counting()));
tally.forEach((c, count) -> System.out.println(c + ": " + count));
}
}
Output
Core Logic
Streaming the string's characters and grouping equal ones together with counting() builds the same tally map in one line, without a manual getOrDefault loop.
- 1
text.chars()streams the string as anIntStreamof character codes;.mapToObj(c -> (char) c)turns each one into a boxedCharacter. - 2
Collectors.groupingBy(c -> c, LinkedHashMap::new, Collectors.counting())groups equal characters together and counts how many are in each group. - 3Passing
LinkedHashMap::newas the map factory keeps the tally in first-seen order, matching the primary approach'sLinkedHashMapchoice. - 4The result is a
Map<Character, Long>— the count is aLong, not anint, sincecounting()always produces one.
"banana"'s characters produces the same tally as the manual version: b: 1, a: 3, n: 2, in first-seen order.Key Point: groupingBy + counting() is the idiomatic modern-Java way to build a frequency map — it reads as 'group by identity, then count', with no manual increment logic at all.
Why: groupingBy() still visits every character once while building the map, which holds one entry per distinct character just like the manual version.