Back to Stream Practice: Employee Analysis
Q35Question 35 of 43

Count vowels and consonants in a string using streams

Key Concepts
  • string.chars() returns an IntStream of Unicode code points (char values)
  • partitioningBy(c -> vowels.contains(c)) splits into vowels (true) and consonants (false)
  • Filter non-letter characters first: Character.isLetter((char) c)
  • Convert to lowercase for case-insensitive counting: Character.toLowerCase((char) c)
  • chars() returns int values, so cast to char for comparisons and display
  • The vowel set is typically Set.of('a', 'e', 'i', 'o', 'u') for constant-time lookup

Definition

Convert the string to an IntStream with chars(), filter for letters, partition by vowel membership, and count each partition.
  • String.chars() returns IntStream of char values (as int).
  • partitioningBy(isVowel, counting()) splits into vowels (true) and consonants (false) with counts.
  • Character.isLetter((char) c) filters out digits, spaces, and punctuation.
  • Character.toLowerCase((char) c) normalizes case for consistent vowel checking.
  • Set.of('a','e','i','o','u') provides O(1) vowel membership checks.

Priya at the National Bank of India validates customer names in the system. She counts vowels and consonants to detect data entry errors (names with zero vowels are likely typos). She scans each character, checks if it is a vowel or consonant, and tallies both counts.

Understanding the Concepts

What does this question ask?

The interviewer wants you to process a string character by character using streams. This tests String.chars(), IntStream operations, partitioning, and character classification.

How do you count vowels and consonants?

JAVA
package com.bank.service;

import java.util.*;
import java.util.stream.Collectors;

public class VowelConsonantCount {
    private static final Set<Character> VOWELS =
        Set.of('a', 'e', 'i', 'o', 'u');

    public static void main(String[] args) {
        String name = "National Bank of India";

        Map<Boolean, Long> counts = name.chars()
            .mapToObj(c -> Character.toLowerCase((char) c))
            .filter(Character::isLetter)
            .collect(Collectors.partitioningBy(
                VOWELS::contains,
                Collectors.counting()));

        System.out.println("Vowels:     " + counts.get(true));
        System.out.println("Consonants: " + counts.get(false));
    }
}
TEXT
Vowels:     7
Consonants: 11

How does String.chars() work?

JAVA
// chars() returns IntStream of char values
"ABC".chars().forEach(c -> System.out.print(c + " "));
// 65 66 67  (int values of 'A', 'B', 'C')

// Cast to char for readable output
"ABC".chars()
    .mapToObj(c -> (char) c)
    .forEach(c -> System.out.print(c + " "));
// A B C

How is partitioningBy useful here?

partitioningBy splits into exactly two groups: true (vowels) and false (consonants). Both keys are guaranteed to exist.

JAVA
Map<Boolean, Long> partition = name.chars()
    .mapToObj(c -> Character.toLowerCase((char) c))
    .filter(Character::isLetter)
    .collect(Collectors.partitioningBy(
        VOWELS::contains, Collectors.counting()));

// partition.get(true)  = vowel count
// partition.get(false) = consonant count

How do you get a detailed character frequency?

JAVA
Map<Character, Long> frequency = name.chars()
    .mapToObj(c -> Character.toLowerCase((char) c))
    .filter(Character::isLetter)
    .collect(Collectors.groupingBy(
        c -> c, Collectors.counting()));

frequency.entrySet().stream()
    .sorted(Map.Entry.<Character, Long>comparingByValue().reversed())
    .forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));
TEXT
a: 3
n: 3
i: 2
l: 1
t: 1
o: 1
b: 1
k: 1
f: 1
d: 1

What are common mistakes?

Mistake 1: forgetting to filter non-letter characters.

JAVA
// WRONG: spaces, digits, and punctuation counted as consonants
Map<Boolean, Long> wrong = name.chars()
    .mapToObj(c -> Character.toLowerCase((char) c))
    .collect(Collectors.partitioningBy(
        VOWELS::contains, Collectors.counting()));
// Spaces and 'of' punctuation end up in false bucket!

// CORRECT: filter letters first
Map<Boolean, Long> correct = name.chars()
    .mapToObj(c -> Character.toLowerCase((char) c))
    .filter(Character::isLetter)  // removes spaces, digits, punctuation
    .collect(Collectors.partitioningBy(
        VOWELS::contains, Collectors.counting()));

Mistake 2: not lowercasing before vowel check.

JAVA
// WRONG: 'A' is not in the lowercase vowel set
Set<Character> vowels = Set.of('a', 'e', 'i', 'o', 'u');
boolean wrong = vowels.contains('A');  // false!

// CORRECT: lowercase first
boolean correct = vowels.contains(Character.toLowerCase('A'));  // true

What about using a regular expression alternative?

JAVA
String lettersOnly = name.replaceAll("[^a-zA-Z]", "");
long vowelCount = lettersOnly.chars()
    .filter(c -> "aeiouAEIOU".indexOf(c) >= 0)
    .count();
long consonantCount = lettersOnly.length() - vowelCount;

What are common interview follow-up questions?

  • "How do you count each vowel separately?" Use groupingBy with the vowel character as key.
  • "How do you find the most frequent character?" Group by character, count, then max by value.
  • "What about Unicode characters beyond BMP?" Use codePoints() instead of chars() for proper handling.

Visual Overview

DIAGRAM
  String: "National Bank of India"
       │
       │  .chars()  → IntStream [78, 97, 116, ...]
       │
       │  .mapToObj(c -> toLowerCase((char) c))
       ▼
  Stream<Character> ['n', 'a', 't', 'i', 'o', 'n', 'a', 'l', ' ', ...]
       │
       │  .filter(isLetter)  → removes spaces
       ▼
  ['n', 'a', 't', 'i', 'o', 'n', 'a', 'l', 'b', 'a', 'n', 'k', ...]
       │
       │  .partitioningBy(VOWELS::contains, counting())
       ▼
  { true: 7 (vowels),  false: 11 (consonants) }

Key Takeaway

Counting vowels and consonants in a string uses chars() to get an IntStream, mapToObj to convert to Character, filter(isLetter) to exclude spaces and punctuation, and partitioningBy(VOWELS::contains, counting()) to split into two counted groups.

The critical steps are lowercase normalization (so 'A' matches the lowercase vowel set) and filtering non-letters (so spaces and digits do not inflate the consonant count). Use Set.of('a','e','i','o','u') for O(1) vowel checks.

For a detailed frequency breakdown, replace partitioningBy with groupingBy(c -> c, counting()) to count each character individually.

Remember one sentence: chars() gives an IntStream of character values, and partitioningBy with a vowel Set splits them into counted vowel and consonant groups in one pass.

---

Share
Use ← → arrow keys to navigate