Back to Strings
Q3Question 3 of 61

Count vowels and consonants in a string

Key Concepts
  • Walk the string once, classify each character as a vowel, a consonant, or neither, using two counters.
  • Lowercase the string first so you only need to test five vowel letters instead of ten.
  • Use Character.isLetter(c) to skip digits, spaces, and punctuation before classifying.
  • A clean vowel test is the expression aeiou.indexOf(c) >= 0 which returns true when the character is found.
  • The algorithm runs in O(n) time and O(1) space because only two integer counters are needed.
  • The classify-and-tally pattern generalises to counting digits, whitespace, or any character category.

Definition

Walk the string once, tally vowels and consonants, and skip anything that is not a letter.
  • Vowel – one of the five letters a, e, i, o, u (case-insensitive).
  • Consonant – any letter that is not a vowel.
  • Algorithm – lowercase the input, loop through each character, check Character.isLetter(c) to skip non-letters, then test if the character is a vowel or a consonant and increment the appropriate counter.
  • Complexity – O(n) time for the single pass and O(1) space for the two counters.
  • Generalisation – the same classify-and-tally pattern works for counting digits, whitespace, or special characters.

National Bank of India wants a quick readability metric on Rajesh's chosen account nickname. A clerk reads each letter of the nickname and makes a tally mark in the vowel column for a, e, i, o, u and in the consonant column for every other letter, skipping any numbers or spaces entirely.

Understanding the Concepts

What does it mean to count vowels and consonants?

In the English alphabet, vowels are the letters a, e, i, o, and u. Every other letter is a consonant. When counting vowels and consonants in a string, you examine each character, decide which category it belongs to, and increment the appropriate counter. Characters that are not letters, such as digits, spaces, and punctuation, are skipped entirely.

JAVA
package com.bank.core;

public class VowelConsonantCount {
    public static void main(String[] args) {
        String nickname = "Rajesh Kumar 2024";
        int vowels = 0, consonants = 0;

        for (char c : nickname.toLowerCase().toCharArray()) {
            if (Character.isLetter(c)) {
                if ("aeiou".indexOf(c) >= 0) {
                    vowels++;
                } else {
                    consonants++;
                }
            }
        }

        System.out.println("Vowels: " + vowels);
        System.out.println("Consonants: " + consonants);
    }
}
TEXT
Vowels: 4
Consonants: 7

Why lowercase the string first?

Lowercasing the string before scanning means you only need to check five characters (a, e, i, o, u) instead of ten (both cases). This simplifies the vowel test and eliminates the risk of forgetting to handle uppercase vowels.

JAVA
package com.bank.core;

public class LowercaseFirst {
    public static void main(String[] args) {
        String input = "PRIYA Sharma";
        String lower = input.toLowerCase();

        int vowels = 0;
        for (char c : lower.toCharArray()) {
            if ("aeiou".indexOf(c) >= 0) {
                vowels++;
            }
        }
        System.out.println("Vowels in '" + input + "': " + vowels);
    }
}
TEXT
Vowels in 'PRIYA Sharma': 4

How do you skip non-letter characters?

The Character.isLetter(c) method returns true only for alphabetic characters. Using it as a guard before the vowel check ensures that digits, spaces, punctuation, and other non-letter characters are ignored. Without this guard, a naive implementation that only checks for vowels and treats everything else as a consonant would wrongly count digits and spaces as consonants.

JAVA
package com.bank.core;

public class IsLetterGuard {
    public static void main(String[] args) {
        String accountTag = "SAV-2001 Rajesh";
        int vowels = 0, consonants = 0, skipped = 0;

        for (char c : accountTag.toLowerCase().toCharArray()) {
            if (!Character.isLetter(c)) {
                skipped++;
            } else if ("aeiou".indexOf(c) >= 0) {
                vowels++;
            } else {
                consonants++;
            }
        }

        System.out.println("Vowels: " + vowels);
        System.out.println("Consonants: " + consonants);
        System.out.println("Skipped: " + skipped);
    }
}
TEXT
Vowels: 3
Consonants: 7
Skipped: 5

How can you test if a character is a vowel efficiently?

There are several ways to test if a character is a vowel. The cleanest approaches are:

  1. "aeiou".indexOf(c) >= 0 : checks if the character exists in the vowel string.
  2. A Set containing the five vowels and calling contains(c).
  3. A switch statement or expression matching the five vowels.
JAVA
package com.bank.core;

import java.util.Set;

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

    public static void main(String[] args) {
        char c = 'a';

        // Method 1: indexOf
        System.out.println("indexOf: " + ("aeiou".indexOf(c) >= 0));

        // Method 2: Set
        System.out.println("Set: " + VOWELS.contains(c));

        // Method 3: switch expression (Java 14+)
        boolean isVowel = switch (c) {
            case 'a', 'e', 'i', 'o', 'u' -> true;
            default -> false;
        };
        System.out.println("Switch: " + isVowel);
    }
}
TEXT
indexOf: true
Set: true
Switch: true

What are the edge cases?

Edge cases include empty strings, strings with no letters at all, strings that are all vowels, and strings that are all consonants.

JAVA
package com.bank.core;

public class VowelEdgeCases {
    public static int[] count(String s) {
        int vowels = 0, consonants = 0;
        for (char c : s.toLowerCase().toCharArray()) {
            if (Character.isLetter(c)) {
                if ("aeiou".indexOf(c) >= 0) vowels++;
                else consonants++;
            }
        }
        return new int[]{vowels, consonants};
    }

    public static void main(String[] args) {
        print("Empty", count(""));
        print("Digits only", count("12345"));
        print("All vowels", count("aeiou"));
        print("All consonants", count("bcdfg"));
        print("Mixed", count("Rajesh"));
    }

    private static void print(String label, int[] result) {
        System.out.println(label + " -> vowels=" + result[0] + ", consonants=" + result[1]);
    }
}
TEXT
Empty -> vowels=0, consonants=0
Digits only -> vowels=0, consonants=0
All vowels -> vowels=5, consonants=0
All consonants -> vowels=0, consonants=5
Mixed -> vowels=2, consonants=4

What about extending this to count more categories?

The classify-and-tally pattern generalises easily. You can extend it to count digits, whitespace, and special characters by adding more counters and more classification branches.

JAVA
package com.bank.service;

public class CharacterAnalyzer {
    public static void main(String[] args) {
        String transactionNote = "EMI: Rs.12,500 for Loan #A2024";
        int vowels = 0, consonants = 0, digits = 0, spaces = 0, special = 0;

        for (char c : transactionNote.toLowerCase().toCharArray()) {
            if (Character.isLetter(c)) {
                if ("aeiou".indexOf(c) >= 0) vowels++;
                else consonants++;
            } else if (Character.isDigit(c)) {
                digits++;
            } else if (c == ' ') {
                spaces++;
            } else {
                special++;
            }
        }

        System.out.println("Vowels: " + vowels);
        System.out.println("Consonants: " + consonants);
        System.out.println("Digits: " + digits);
        System.out.println("Spaces: " + spaces);
        System.out.println("Special: " + special);
    }
}
TEXT
Vowels: 5
Consonants: 8
Digits: 7
Spaces: 4
Special: 5

What about using Java streams?

You can use Java streams for a more functional approach. The chars() method on String returns an IntStream that you can filter and count. This is more concise but slightly slower due to boxing overhead.

JAVA
package com.bank.core;

public class VowelCountStream {
    public static void main(String[] args) {
        String name = "Pankaj Architect";
        String lower = name.toLowerCase();

        long vowels = lower.chars()
            .filter(c -> Character.isLetter(c))
            .filter(c -> "aeiou".indexOf(c) >= 0)
            .count();

        long consonants = lower.chars()
            .filter(c -> Character.isLetter(c))
            .filter(c -> "aeiou".indexOf(c) < 0)
            .count();

        System.out.println("Vowels: " + vowels);
        System.out.println("Consonants: " + consonants);
    }
}
TEXT
Vowels: 5
Consonants: 9

What is the time and space complexity?

The algorithm makes a single pass through the string, visiting each character exactly once. The vowel test is O(1) because the set of vowels is fixed at five characters. The total time complexity is O(n) where n is the length of the string. The space complexity is O(1) because only a few integer counters are used, regardless of the input size.

If you call toLowerCase() first, that creates a new String of size n, adding O(n) space. To stay at O(1) space, you can compare each character against both cases inline instead of lowercasing the whole string.

What are common mistakes?

The most common mistake is not guarding with isLetter() and treating every non-vowel character as a consonant. This wrongly counts digits, spaces, and punctuation as consonants.

JAVA
package com.bank.core;

public class VowelMistake {
    public static void main(String[] args) {
        String input = "SAV 2001";

        // WRONG: no isLetter guard
        int wrongConsonants = 0;
        for (char c : input.toLowerCase().toCharArray()) {
            if ("aeiou".indexOf(c) < 0) {
                wrongConsonants++; // counts space and digits too
            }
        }
        System.out.println("Wrong consonants: " + wrongConsonants);

        // CORRECT: isLetter guard
        int correctConsonants = 0;
        for (char c : input.toLowerCase().toCharArray()) {
            if (Character.isLetter(c) && "aeiou".indexOf(c) < 0) {
                correctConsonants++;
            }
        }
        System.out.println("Correct consonants: " + correctConsonants);
    }
}
TEXT
Wrong consonants: 6
Correct consonants: 2

Visual Overview

DIAGRAM
  Character Classification Flow
  ═════════════════════════════

  Input: "SAV 2001"

  For each character c:
  ┌─────────────────────┐
  │  Is c a letter?     │
  │  isLetter(c)        │
  └─────────┬───────────┘
        yes │         no
            ▼          ▼
  ┌─────────────┐  ┌────────┐
  │ Is vowel?   │  │ SKIP   │
  │ aeiou has c │  │(digit, │
  └──┬──────┬───┘  │space)  │
  yes│      │no    └────────┘
     ▼      ▼
  vowels++ consonants++

Key Takeaway

Counting vowels and consonants is a single linear pass with two counters. Lowercase the string first so you only have to test five vowel letters, then loop through each character. For each character, first check whether it is a letter at all, and if it is not a letter, skip it so digits, spaces, and punctuation do not pollute the counts. If it is a letter, test whether it is one of a, e, i, o, u, incrementing the vowel counter if so and the consonant counter otherwise.

In the banking project at National Bank of India, Rajesh used this in an analytics panel that shows how many vowels and consonants a customer's account nickname contains. The isLetter() guard is the critical detail, because nicknames frequently contain digits and spaces, and a naive implementation that only checks for vowels and treats everything else as a consonant would wrongly count those non-letters.

The pattern generalises easily to counting digits, whitespace, or any character category by adding more counters and more classification branches. For a functional approach, Java streams with chars() and filter() offer concise code at the cost of slightly more overhead.

Remember one sentence: lowercase first, guard with isLetter(), then classify each character as a vowel or consonant in a single O(n) pass.

---

Share
Use ← → arrow keys to navigate