Count Character Occurrences Using Streams in Java
Problem
String.chars() turns a string into a stream of character codes, so the usual grouping collectors can count them.
Given a word, count how many times each character appears and print the counts in alphabetical order.
Java Program
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.stream.Collectors;
public class CountCharactersUsingStreams {
public static void main(String[] args) {
String text = "mississippi";
Map<Character, Long> counts = text.chars() // an IntStream of character codes
.mapToObj(c -> (char) c) // box each code as a Character
.collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.counting()));
System.out.println(counts);
}
}Output
Core Logic
Turning the string into a stream of characters lets groupingBy with counting() build the whole frequency table in one expression.
- 1
text.chars()returns anIntStreamof character codes. - 2
mapToObj(c -> (char) c)converts each code into aCharacterobject. - 3
groupingBy(Function.identity(), TreeMap::new, counting())groups equal characters and counts them. - 4Printing the
TreeMaplists characters in alphabetical order.
i and s each appear 4 times, p twice and m once.Key Point: Forgetting mapToObj leaves you with numbers: grouping the raw IntStream isn't possible without boxing.
Why: Each of the n characters is inserted into a TreeMap with k distinct keys.
Key Concepts
Approach 2: Most Frequent Character
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class CountCharactersMostFrequent {
public static void main(String[] args) {
String text = "mississippi";
Map.Entry<Character, Long> top = text.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream()
.max((a, b) -> a.getValue().equals(b.getValue())
? Character.compare(b.getKey(), a.getKey()) // tie: the earlier letter counts as "bigger"
: Long.compare(a.getValue(), b.getValue())) // otherwise the higher count wins
.get();
System.out.println("Most frequent: '" + top.getKey() + "' (" + top.getValue() + " times)");
}
}
Output
Core Logic
After counting, streaming the map's entries and taking the maximum by count finds the most frequent character.
- 1The counts are built with a plain
groupingByandcounting(). - 2The entry stream is reduced with
maxand a comparator. - 3The comparator compares counts first and, on a tie, prefers the earlier letter.
i and s both appear 4 times; the tie-break picks i.Key Point: Always define a tie-break — otherwise which of several equally frequent characters you get depends on the map's iteration order.
Why: One pass counts the characters and another scans the k distinct entries.
Key Concepts
Interview Questions
How would you ignore case and spaces?
Lower-case the string and filter before grouping: text.toLowerCase().chars().filter(c -> c != ' ').
How do you find characters that appear only once?
Filter the counted map's entries for a value of 1. Use a LinkedHashMap factory to keep them in the order they first appeared.