Introduction to Vowel and Consonant Counting

Counting vowels and consonants is a classic programming exercise that appears frequently in:

  • Technical interviews
  • Coding bootcamp assignments
  • Data validation routines
  • Text analysis applications
  • String processing algorithms

While seemingly simple on the surface, this problem introduces important concepts about character classification, string iteration, and algorithm optimization.

This comprehensive guide explores three different approaches, from basic to advanced, each with its own performance characteristics and use cases.

Advertisement

Understanding Vowels vs Consonants

What Are Vowels?

Vowels are the letters a, e, i, o, u (and sometimes y).

In standard English:

  • Pure vowels: a, e, i, o, u
  • Sometimes vowel: y (for example, gym vs yellow)
  • Both uppercase and lowercase vowels are considered.

What Are Consonants?

Consonants are all other alphabetic characters.

Examples include:

  • b, c, d, f, g, h, etc.

Consonants are:

  • Any alphabetic letter that is not a vowel.
  • The remaining 21 letters of the English alphabet.
  • Counted regardless of case.

Important Considerations

  • Numbers are neither vowels nor consonants.
    • Example: "abc123" contains:
      • Vowels: 1 (a)
      • Consonants: 2 (b, c)
      • Other characters: 3 (1, 2, 3)
  • Spaces and punctuation are generally ignored.
  • Unicode characters may require special handling for international languages.
  • Comparisons are usually performed in a case-insensitive manner.

Example Breakdown

For the string:

 
Hello World
 

Character classification:

 
String: H e l l o   W o r l d
Type:   C V C C V S C V C C C

C = Consonant
V = Vowel
S = Space
 

Count:

  • Vowels: e, o, o = 3
  • Consonants: H, l, l, W, r, l, d = 7
  • Other: space = 1

Method 1: Loop with Individual Character Comparisons

The most straightforward approach is to iterate through every character and compare it individually.

 
public class CountVowelsConsonantsMethod1 {

    public static void main(String[] args) {

        String input = "Automation";

        int vowels = 0;
        int consonants = 0;

        // Convert to lowercase
        String lower = input.toLowerCase();

        // Iterate through every character
        for (int i = 0; i < lower.length(); i++) {

            char ch = lower.charAt(i);

            // Process only alphabetic characters
            if (ch >= 'a' && ch <= 'z') {

                // Check whether character is a vowel
                if (ch == 'a'
                        || ch == 'e'
                        || ch == 'i'
                        || ch == 'o'
                        || ch == 'u') {

                    vowels++;

                } else {

                    consonants++;
                }
            }
        }

        System.out.println("String: " + input);
        System.out.println("Vowels: " + vowels);
        System.out.println("Consonants: " + consonants);
    }
}
 

Output

 
String: Automation
Vowels: 5
Consonants: 5
 

Step-by-Step Explanation

1. Convert to Lowercase

 
String lower = input.toLowerCase();
 

This ensures that both uppercase and lowercase vowels are treated identically.


2. Iterate Through Every Character

 
for (int i = 0; i < lower.length(); i++) {

    char ch = lower.charAt(i);
}
 

Each character is accessed one at a time using charAt(i).


3. Check Whether the Character Is a Letter

 
if (ch >= 'a' && ch <= 'z') {

    // Alphabetic character
}
 

This skips numbers, spaces, punctuation marks, and symbols.


4. Identify Whether It Is a Vowel

 
if (ch == 'a'
        || ch == 'e'
        || ch == 'i'
        || ch == 'o'
        || ch == 'u') {

    vowels++;

} else {

    consonants++;
}
 

If the character matches one of the five vowels, increment the vowel counter.

Otherwise, increment the consonant counter.


Internal Process

Input:

 
Automation
 

After converting to lowercase:

 
automation
 

Iteration:

 
i = 0
'a'
Vowel
vowels = 1

i = 1
'u'
Vowel
vowels = 2

i = 2
't'
Consonant
consonants = 1

i = 3
'o'
Vowel
vowels = 3

i = 4
'm'
Consonant
consonants = 2

i = 5
'a'
Vowel
vowels = 4

i = 6
't'
Consonant
consonants = 3

i = 7
'i'
Vowel
vowels = 5

i = 8
'o'
Vowel
vowels = 6

i = 9
'n'
Consonant
consonants = 4
 

Note: Recalculating the string "Automation" shows:

  • Vowels: a, u, o, a, i, o = 6
  • Consonants: t, m, t, n = 4

This means the earlier sample output (5 vowels, 5 consonants) is incorrect. The actual count for "Automation" is:

 
String: Automation
Vowels: 6
Consonants: 4
 

Advantages

  • Simple and beginner-friendly.
  • Easy to understand.
  • No additional data structures are required.
  • Frequently asked in Java interviews.

Disadvantages

  • Requires multiple if comparisons for each character.
  • Slightly more verbose than newer approaches.
  • Can become less efficient for very large strings.

Performance Characteristics

  • Time Complexity: O(n), where n is the length of the string.
  • Space Complexity: O(1)
  • Best Case: O(n), since every character must be processed.

Method 2: Optimized Approach Using indexOf()

This method uses the indexOf() method to determine whether a character is a vowel.

 
public class CountVowelsConsonantsMethod2 {

    public static void main(String[] args) {

        String input = "Automation";

        int vowels = 0;
        int consonants = 0;

        String vowelSet = "aeiouAEIOU";

        // Iterate through each character
        for (char ch : input.toCharArray()) {

            // Process only alphabetic characters
            if (Character.isLetter(ch)) {

                // Check whether the character exists in the vowel set
                if (vowelSet.indexOf(ch) != -1) {

                    vowels++;

                } else {

                    consonants++;
                }
            }
        }

        System.out.println("String: " + input);
        System.out.println("Vowels: " + vowels);
        System.out.println("Consonants: " + consonants);
    }
}
 

Output

 
String: Automation
Vowels: 6
Consonants: 4
 

Key Improvements

Vowel Set

 
String vowelSet = "aeiouAEIOU";
 

The string contains every vowel in both lowercase and uppercase.


indexOf() Method

 
vowelSet.indexOf(ch)
 

The method returns:

  • An index (0–9) if the character exists in the vowel set.
  • -1 if the character is not found.

Character.isLetter()

 
Character.isLetter(ch)
 

The built-in Character.isLetter() method checks whether the character is alphabetic.

This automatically ignores:

  • Numbers
  • Spaces
  • Punctuation
  • Symbols

How indexOf() Works

Given:

 
String vowelSet = "aeiouAEIOU";
 

Examples:

 
indexOf('a') → 0

indexOf('e') → 1

indexOf('z') → -1
 

Advantages

  • Cleaner than multiple if conditions.
  • Uses Java's built-in Character.isLetter() method.
  • Easier to read.
  • Supports uppercase and lowercase characters without converting the input string.

Disadvantages

  • indexOf() performs a linear search through the vowel string.
  • Complexity is O(k) where k = 10, although this is effectively constant for practical purposes.

Performance Characteristics

  • Time Complexity: O(n × 10) = O(n)
  • Space Complexity: O(1)
  • Real-world performance is nearly identical to Method 1.

Method 3: Modern Stream API Method

This approach uses Java Streams and functional programming.

 
public class CountVowelsConsonantsMethod3 {

    public static void main(String[] args) {

        String input = "Automation";

        // Count vowels
        long vowels = input.toLowerCase()
                           .chars()
                           .filter(ch -> "aeiou".indexOf(ch) >= 0)
                           .count();

        // Count total alphabetic characters
        long totalLetters = input.chars()
                                 .filter(Character::isLetter)
                                 .count();

        long consonants = totalLetters - vowels;

        System.out.println("String: " + input);
        System.out.println("Vowels: " + vowels);
        System.out.println("Consonants: " + consonants);
    }
}
 

Output

 
String: Automation
Vowels: 6
Consonants: 4
 

Stream Pipeline Explanation

chars()

 
input.chars()
 

Converts the string into an IntStream of Unicode character values.


filter()

 
.filter(ch -> "aeiou".indexOf(ch) >= 0)
 

Keeps only characters that satisfy the given condition.


count()

 
.count()
 

Returns the total number of remaining elements.


Alternative Concise Version

 
public class CountVowelsConsonantsStreams {

    private static final String VOWELS = "aeiouAEIOU";

    public static void countVowelsConsonants(String input) {

        long vowels = input.chars()
                           .filter(ch -> VOWELS.indexOf(ch) >= 0)
                           .count();

        long consonants = input.chars()
                               .filter(Character::isLetter)
                               .filter(ch -> VOWELS.indexOf(ch) < 0)
                               .count();

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

    public static void main(String[] args) {

        countVowelsConsonants("Hello World");
    }
}
 

Advantages

  • Modern Java coding style.
  • Concise and expressive.
  • Supports functional programming.
  • Easy to combine with additional stream operations.

Disadvantages

  • Less intuitive for beginners.
  • Slight overhead for small strings.
  • Can appear more "magical" than loop-based solutions.

Performance Characteristics

  • Time Complexity: O(n)
  • Space Complexity: O(n) for the stream pipeline.
  • Slightly slower than loops for small strings.
  • More useful when working with parallel streams and large datasets.

When to Use Streams

Streams are suitable when:

  • Processing large collections of text.
  • Using parallel streams.
  • Following a functional programming style.
  • Working in modern Java applications.

Handling Edge Cases

Case 1: Strings with Numbers and Special Characters

 
public static void countVowelsConsonantsStrict(String input) {

    String vowels = "aeiouAEIOU";

    int vowelCount = 0;
    int consonantCount = 0;

    for (char ch : input.toCharArray()) {

        if (Character.isLetter(ch)) {

            if (vowels.indexOf(ch) >= 0) {

                vowelCount++;

            } else {

                consonantCount++;
            }
        }

        // Numbers, punctuation, and spaces are ignored.
    }

    System.out.println("Vowels: " + vowelCount);
    System.out.println("Consonants: " + consonantCount);
}
 

Test

 
countVowelsConsonantsStrict("Hello123!@# World");
 

Output

 
Vowels: 3
Consonants: 7
 

Case 2: Separate Counts for Uppercase and Lowercase

 
public static void countVowelsConsonantsDetailed(String input) {

    int upperVowels = 0;
    int lowerVowels = 0;

    int upperConsonants = 0;
    int lowerConsonants = 0;

    String vowels = "aeiouAEIOU";

    for (char ch : input.toCharArray()) {

        if (Character.isLetter(ch)) {

            boolean isVowel = vowels.indexOf(ch) >= 0;

            boolean isUppercase =
                    Character.isUpperCase(ch);

            if (isVowel) {

                if (isUppercase) {
                    upperVowels++;
                } else {
                    lowerVowels++;
                }

            } else {

                if (isUppercase) {
                    upperConsonants++;
                } else {
                    lowerConsonants++;
                }
            }
        }
    }

    System.out.println("Upper Vowels: " + upperVowels);
    System.out.println("Lower Vowels: " + lowerVowels);
    System.out.println("Upper Consonants: " + upperConsonants);
    System.out.println("Lower Consonants: " + lowerConsonants);
}
 

Test

 
countVowelsConsonantsDetailed("HeLLo WoRLD");
 

Case 3: Using a Reusable Class

 
public class VowelConsonantCounter {

    private final String vowels = "aeiouAEIOU";

    private int vowelCount;
    private int consonantCount;

    public void count(String input) {

        vowelCount = 0;
        consonantCount = 0;

        if (input == null || input.isEmpty()) {
            return;
        }

        for (char ch : input.toCharArray()) {

            if (Character.isLetter(ch)) {

                if (vowels.indexOf(ch) >= 0) {

                    vowelCount++;

                } else {

                    consonantCount++;
                }
            }
        }
    }

    public int getVowels() {
        return vowelCount;
    }

    public int getConsonants() {
        return consonantCount;
    }

    public int getTotal() {
        return vowelCount + consonantCount;
    }
}
 

Usage

 
VowelConsonantCounter counter =
        new VowelConsonantCounter();

counter.count("Automation");

System.out.println("Vowels: " + counter.getVowels());

System.out.println("Consonants: " + counter.getConsonants());

Real-World Applications

Application 1: Password Strength Validation

 
public class PasswordValidator {

    public static boolean hasBalancedVowelsConsonants(String password) {

        VowelConsonantCounter counter =
                new VowelConsonantCounter();

        counter.count(password);

        int vowels = counter.getVowels();
        int consonants = counter.getConsonants();
        int total = counter.getTotal();

        // Password should contain at least 20% vowels
        return (vowels * 100) / total >= 20;
    }

    public static void main(String[] args) {

        System.out.println(
                hasBalancedVowelsConsonants("Passw0rd"));

        System.out.println(
                hasBalancedVowelsConsonants("MyS3cur3P@ss"));
    }
}
 

Application 2: Text Readability Analysis

 
public class ReadabilityAnalyzer {

    public static void analyzeText(String text) {

        VowelConsonantCounter counter =
                new VowelConsonantCounter();

        counter.count(text);

        int vowels = counter.getVowels();
        int consonants = counter.getConsonants();
        int total = counter.getTotal();

        if (total == 0) {
            return;
        }

        double vowelPercentage =
                (vowels * 100.0) / total;

        System.out.println("Text Analysis:");
        System.out.println("Total letters: " + total);

        System.out.println(
                "Vowels: "
                        + vowels
                        + " ("
                        + String.format("%.1f", vowelPercentage)
                        + "%)");

        System.out.println(
                "Consonants: " + consonants);

        // Optimal readability: 38%–40% vowels
        if (vowelPercentage >= 35
                && vowelPercentage <= 45) {

            System.out.println("Readability: GOOD");

        } else if (vowelPercentage < 35) {

            System.out.println(
                    "Readability: TOO CONSONANT-HEAVY");

        } else {

            System.out.println(
                    "Readability: TOO VOWEL-HEAVY");
        }
    }

    public static void main(String[] args) {

        analyzeText(
                "The quick brown fox jumps over the lazy dog");
    }
}
 

Common Mistakes

Mistake 1: Forgetting to Convert to Lowercase

❌ Wrong

 
String input = "AEIOUaeiou";

String vowels = "aeiou";

int count = 0;

for (char ch : input.toCharArray()) {

    if (vowels.indexOf(ch) >= 0) {

        count++;
    }
}

System.out.println(count);

// Output: 5
 

✅ Right

 
String input = "AEIOUaeiou";

String vowels = "aeiouAEIOU";

int count = 0;

for (char ch : input.toCharArray()) {

    if (vowels.indexOf(ch) >= 0) {

        count++;
    }
}

System.out.println(count);

// Output: 10
 

Mistake 2: Counting Non-Alphabetic Characters

❌ Wrong

 
for (char ch : input.toCharArray()) {

    if (vowels.indexOf(ch) >= 0) {

        vowels++;

    } else {

        consonants++;
    }
}
 

This incorrectly counts:

  • Numbers
  • Spaces
  • Symbols
  • Punctuation

as consonants.

✅ Right

 
for (char ch : input.toCharArray()) {

    if (Character.isLetter(ch)) {

        if (vowels.indexOf(ch) >= 0) {

            vowelCount++;

        } else {

            consonantCount++;
        }
    }
}
 

Mistake 3: Not Handling Empty Strings

❌ Wrong

 
String input = "";

int vowels = 0;

for (char ch : input.toCharArray()) {

}

System.out.println(
        vowels / input.length());
 

This may result in an ArithmeticException.

✅ Right

 
String input = "";

if (input == null || input.isEmpty()) {

    System.out.println(
            "Input cannot be empty");

    return;
}

// Safe to continue
 

Frequently Asked Questions

Q1: Should I count 'y' as a vowel?

Answer: Usually no, unless your requirements specifically state otherwise. Although 'y' sometimes functions as a vowel, it is generally treated as a consonant for consistency.


Q2: How do I handle Unicode characters like é or ñ?

Answer: In English, these characters are generally treated as consonants. If your application supports multiple languages, you should define language-specific vowel sets.


Q3: Which method is the fastest?

Answer: For small strings, Method 1 and Method 2 perform almost identically. Method 3 (Streams) introduces additional overhead but becomes useful when processing large datasets or parallel streams.


Q4: Can I use regular expressions to count vowels?

Yes.

 
long count = input
        .replaceAll("[^aeiouAEIOU]", "")
        .length();
 

Q5: How do I calculate the percentage of vowels?

 
double percentage =
        (vowels * 100.0) / totalLetters;
 

Q6: What's the difference between char and Character?

Answer:

  • char is a primitive data type.
  • Character is the wrapper class that provides useful utility methods such as:
    • Character.isLetter()
    • Character.isDigit()
    • Character.isUpperCase()
    • Character.toLowerCase()

Q7: How do I make the program case-insensitive?

Answer: You can either:

  • Convert the input string to lowercase once before processing, or
  • Include both uppercase and lowercase vowels in the lookup string.

Example:

 
String vowels = "aeiouAEIOU";
 

Q8: Can I use a HashSet instead of indexOf()?

Yes.

 
Set<Character> vowels =
        new HashSet<>(
                Arrays.asList(
                        'a','e','i','o','u',
                        'A','E','I','O','U'));

if (vowels.contains(ch)) {

    // Vowel
}
 

Using a HashSet provides faster lookups when the lookup set becomes large.


Q9: How do I test this program?

Create unit tests.

 
@Test
public void testVowelCounting() {

    counter.count("aeiou");

    assertEquals(
            5,
            counter.getVowels());
}
 

Q10: What if I need to count vowels in multiple strings?

Answer: Reuse the same counter object or create a reusable static utility method. This avoids repeatedly creating new objects and improves efficiency.