Definition
- String.chars() returns
IntStreamof 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?
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));
}
}
Vowels: 7
Consonants: 11
How does String.chars() work?
// 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.
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?
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()));
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.
// 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.
// 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?
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
groupingBywith 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 ofchars()for proper handling.
Visual Overview
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.
---