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

  1. Create HashMap.
  2. Convert string into characters.
  3. Traverse characters.
  4. 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:

  1. Convert String into character stream.
  2. Use mapToObj() to convert int values to Character.
  3. Use groupingBy() with counting().
  4. Use LinkedHashMap when order matters.
  5. 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.