Character Frequency Using Streams
Java coding interview problem for Java 8 Streams: Character Frequency Using Streams.
Finding the frequency of characters in a string is one of the most common Java coding interview problems.
This problem helps understand:
- HashMap frequency pattern
- Stream API
- Collectors
- Lambda expressions
- Functional programming
- Data analysis techniques
What is Character Frequency?
Character frequency means counting how many times each character appears in a string.
Example:
Input:
programming
Frequency:
p → 1
r → 2
o → 1
g → 2
m → 2
i → 1
n → 1
Understanding Frequency Counting
The basic idea:
Read Character
↓
Check Existing Count
↓
Increase Count
↓
Store Result
Example
Input:
hello
Process:
First character:
h
Map:
h → 1
Second character:
e
Map:
h → 1
e → 1
Third character:
l
Map:
h → 1
e → 1
l → 1
Fourth character:
l
Already exists:
l → 2
Fifth character:
o
Final:
h → 1
e → 1
l → 2
o → 1
Why Character Frequency Problems Are Important
Character frequency is a foundation for many advanced problems.
It tests:
1. HashMap Knowledge
Understanding:
Character → Count
mapping.
2. Stream API Skills
Using:
- stream()
- collect()
- groupingBy()
- counting()
3. Data Processing
Frequency analysis is used in:
- Analytics
- Search systems
- Text processing
- Compression algorithms
HashMap Frequency Pattern
The classic approach:
Map<Character,Integer>
Example:
String:
apple
Map:
a → 1
p → 2
l → 1
e → 1
Stream API Overview
Java Stream API provides a functional way to process collections.
Stream flow:
Source
↓
Intermediate Operations
↓
Terminal Operation
Example:
string.chars()
creates:
IntStream
Then:
filter()
map()
collect()
process data.
Stream Pipeline Concept
Example:
text.chars()
.mapToObj()
.collect()
Flow:
String
↓
Characters
↓
Transform
↓
Group
↓
Frequency Map
Real-World Applications
Text Analytics
Count:
- Words
- Characters
- Symbols
Search Engines
Analyze:
- Query patterns
- Keyword frequency
Data Compression
Algorithms like:
Huffman Coding
use frequency information.
Log Processing
Count:
- Error codes
- Event types
- Message frequency
Problem Statement
Given a string, find the frequency of each character using Java Streams.
Example 1
Input:
"hello"
Output:
h=1
e=1
l=2
o=1
Example 2
Input:
"java"
Output:
j=1
a=2
v=1
Character Frequency Visualization
Input:
banana
Characters:
b
a
n
a
n
a
Count:
b → 1
a → 3
n → 2
Approach 1 — Traditional HashMap Approach
Before Streams, the common solution uses a loop.
Algorithm
- Create HashMap.
- Convert string into characters.
- Traverse characters.
- Update count.
Java Program — HashMap Approach
import java.util.*;
public class CharacterFrequency {
public static Map<Character,Integer>
findFrequency(String text) {
Map<Character,Integer> frequency =
new HashMap<>();
for(char ch : text.toCharArray()) {
frequency.put(
ch,
frequency.getOrDefault(
ch,
0
) + 1
);
}
return frequency;
}
public static void main(String[] args) {
String text = "programming";
System.out.println(
findFrequency(text)
);
}
}
Output
Example:
{
p=1,
r=2,
o=1,
g=2,
m=2,
i=1,
n=1
}
Step-by-Step Explanation
Input:
hello
Initial:
{}
Read:
h
Insert:
h=1
Read:
e
Insert:
e=1
Read:
l
Insert:
l=1
Read:
l
Existing value:
l=1
Update:
l=2
Read:
o
Insert:
o=1
Final:
h=1
e=1
l=2
o=1
Approach 2 — Using Streams and groupingBy()
Java Streams provide a cleaner frequency counting approach.
The main collector:
Collectors.groupingBy()
Stream Flow
String
↓
chars()
↓
Character Stream
↓
groupingBy()
↓
Counting
Java Program — Streams Approach
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class CharacterFrequencyUsingStreams {
public static Map<Character,Long>
findFrequency(String text) {
return text.chars()
.mapToObj(
c -> (char)c
)
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
}
public static void main(String[] args) {
String text = "hello";
System.out.println(
findFrequency(text)
);
}
}
Output
{
h=1,
e=1,
l=2,
o=1
}
Step-by-Step Stream Explanation
Input:
hello
Step 1
Convert characters:
h
e
l
l
o
Step 2
Grouping:
h → group
e → group
l → group
l → same group
o → group
Step 3
Counting:
h → 1
e → 1
l → 2
o → 1
Understanding Function.identity()
Code:
Function.identity()
means:
Return the same object
Example:
character -> character
is equivalent to:
Function.identity()
In:
groupingBy(
Function.identity()
)
characters become:
Map Keys
Understanding Collectors.counting()
Collector:
Collectors.counting()
counts elements in each group.
Example:
Group:
l
l
Count:
2
Complexity Analysis
Let:
n = string length
Traversal:
O(n)
HashMap insertion:
Average:
O(1)
Total Time:
O(n)
Space:
O(k)
where:
k = number of unique characters
Advantages
- Clean functional style.
- Less manual code.
- Uses Stream API.
- Easy to extend.
Drawbacks
- Slightly more memory.
- Stream syntax requires understanding.
- Debugging can be harder for beginners.
Handling Uppercase and Lowercase Characters
Important question:
Should these be different?
Example:
Java
Characters:
J
a
v
a
Frequency:
J=1
a=2
v=1
If case-insensitive:
Convert:
text.toLowerCase()
Example:
text.toLowerCase()
.chars()
Handling Spaces and Special Characters
Input:
hello world
includes:
space
To ignore spaces:
.filter(
ch -> ch != ' '
)
Using Function.identity() in Character Frequency
Function.identity() is commonly used with Stream collectors.
Example:
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
This is equivalent to:
Collectors.groupingBy(
character -> character,
Collectors.counting()
)
Why Use Function.identity()?
It improves readability when:
Input value
↓
Same value becomes key
Example:
Characters:
j
a
v
a
Grouping:
j → 1
a → 2
v → 1
Character Frequency With Map<Character,Long>
The Stream approach returns:
Map<Character,Long>
because:
Collectors.counting()
returns:
Long
Example:
Map<Character,Long> frequency =
text.chars()
.mapToObj(
c -> (char)c
)
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
);
Output:
{
j=1,
a=2,
v=1
}
Finding Most Frequent Character
Problem:
Find the character appearing maximum times.
Example:
Input:
programming
Frequency:
g → 2
r → 2
m → 2
Approach
Character Frequency
↓
Find Maximum Count
↓
Return Character
Java Program
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class MostFrequentCharacter {
public static Character findMostFrequent(
String text) {
return text.chars()
.mapToObj(
c -> (char)c
)
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
)
.entrySet()
.stream()
.max(
Map.Entry.comparingByValue()
)
.map(
Map.Entry::getKey
)
.orElse(null);
}
}
Dry Run
Input:
apple
Frequency:
a → 1
p → 2
l → 1
e → 1
Maximum:
p → 2
Result:
p
Finding First Non-Repeating Character
A very common interview problem:
Find the first character that appears only once.
Example:
Input:
swiss
Frequency:
s → 3
w → 1
i → 1
First non-repeating:
w
Java Program
public static Character firstNonRepeating(
String text) {
Map<Character,Long> frequency =
text.chars()
.mapToObj(
c -> (char)c
)
.collect(
Collectors.groupingBy(
Function.identity(),
LinkedHashMap::new,
Collectors.counting()
)
);
return frequency.entrySet()
.stream()
.filter(
entry ->
entry.getValue() == 1
)
.map(
Map.Entry::getKey
)
.findFirst()
.orElse(null);
}
Why LinkedHashMap?
Normal HashMap:
No ordering guarantee
But first non-repeating character depends on:
Original order
Example:
Input:
aabbcd
Frequency:
a → 2
b → 2
c → 1
d → 1
Need:
c
because it appears first.
Therefore:
Use:
LinkedHashMap
Finding Duplicate Characters
Problem:
Find characters appearing more than once.
Example:
Input:
programming
Output:
r
g
m
Java Program
List<Character> duplicates =
text.chars()
.mapToObj(
c -> (char)c
)
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
)
.entrySet()
.stream()
.filter(
entry ->
entry.getValue() > 1
)
.map(
Map.Entry::getKey
)
.toList();
Sorting Characters by Frequency
Example:
Input:
banana
Frequency:
a → 3
n → 2
b → 1
Sort descending:
a
n
b
Java Program
Map<Character,Long> sortedFrequency =
frequency.entrySet()
.stream()
.sorted(
Map.Entry
.<Character,Long>
comparingByValue()
.reversed()
)
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(a,b)->a,
LinkedHashMap::new
)
);
Unicode Character Handling
Java char uses:
UTF-16
For basic English:
char
works.
Example:
hello
For complete Unicode support:
Use:
codePoints()
instead of:
chars()
Example
text.codePoints()
.mapToObj(
code ->
String.valueOf(
(char)code
)
)
Character vs String Frequency
Character Frequency
Example:
hello
Result:
h=1
e=1
l=2
o=1
Word Frequency
Example:
java spring java
Result:
java=2
spring=1
Word frequency uses:
split()
instead of:
chars()
Stream vs Loop Comparison
| Feature | Loop | Stream |
|---|---|---|
| Code size | More | Less |
| Performance | Slightly faster | Comparable |
| Readability | Simple | Functional |
| Parallel support | Manual | Built-in |
| Debugging | Easy | Requires practice |
HashMap Internal Working
Frequency counting uses:
HashMap
When storing:
map.put(character,count)
Java performs:
Character
↓
hashCode()
↓
Bucket
↓
Store Count
When duplicate character arrives:
Find existing bucket
↓
Update value
↓
Increase count
Parallel Streams Considerations
Example:
text.parallelStream()
Frequency counting requires shared state.
Avoid:
HashMap
with parallel modifications.
Prefer:
Collectors
which handle reduction safely.
Example:
text.chars()
.parallel()
.mapToObj(
c -> (char)c
)
.collect(
Collectors.groupingByConcurrent(
Function.identity(),
Collectors.counting()
)
);
Common Interview Mistakes
Mistake 1
Using:
distinct()
to count frequency.
Wrong:
distinct removes duplicates
Mistake 2
Using HashMap for first non-repeating character.
Problem:
Order is lost
Use:
LinkedHashMap
Mistake 3
Ignoring case sensitivity.
Example:
Java
java
Different characters:
J
j
Mistake 4
Ignoring spaces.
Input:
hello world
contains:
space character
Edge Cases
| Case | Handling |
|---|---|
| Empty String | Return empty map |
| Single Character | Frequency = 1 |
| All Same Characters | One entry |
| Spaces | Filter if required |
| Unicode | Use codePoints() |
Interview Follow-up Questions
Q1. Count character frequency using Streams.
Q2. Find first non-repeating character.
Q3. Find most frequent character.
Q4. Find duplicate characters.
Q5. Sort characters by frequency.
Q6. Difference between HashMap and LinkedHashMap.
Q7. How does groupingBy work internally?
Related Java Collection Problems
- Find Duplicate Elements Using Streams
- Count Word Frequency Using HashMap
- Find First Non-Repeating Character
- Group Employees by Department
- Sort Map by Value
- Find Intersection of Two Lists
Key Takeaways
Character frequency follows:
String
↓
Characters
↓
Grouping
↓
Counting
↓
Frequency Map
Recommended approaches:
Modern Java
Use:
Stream
+
Collectors.groupingBy()
+
counting()
Preserve Order
Use:
LinkedHashMap
Unicode Support
Use:
codePoints()
Complexity:
Time:
O(n)
Space:
O(k)
where:
k = unique characters
Frequently Asked Interview Questions
Q1. Why use groupingBy()?
It groups identical characters together.
Q2. Why counting() returns Long?
Because Collector API uses long counting internally.
Q3. Why LinkedHashMap for first non-repeating?
Because insertion order matters.
Q4. How are duplicate characters detected?
By checking:
frequency > 1
Interview Tip
When asked:
"Find character frequency using Streams."
Explain:
- Convert String into character stream.
- Use
mapToObj()to convert int values to Character. - Use
groupingBy()withcounting(). - Use LinkedHashMap when order matters.
- Discuss Unicode and performance considerations.
For senior Java interviews, discuss:
- Stream collectors.
- HashMap internals.
- LinkedHashMap ordering.
- Unicode handling.
- Parallel stream considerations.
This demonstrates strong understanding of Java Streams, Collections, and text processing patterns.