Back to Stream: filter
Q19Question 19 of 19

Filter a list of strings to those containing ONLY vowels

Key Concepts
  • String.matches(regex) tests whether the entire string matches, anchoring implicitly at start and end
  • The character class [aeiouAEIOU]+ matches one or more vowels of either case
  • Empty strings fail because + requires at least one character
  • Precompile the Pattern for hot paths to avoid recompilation on every call
  • matches anchors the whole string; find would match a vowel substring, which is not the same
  • Decide whether y and accented vowels belong in the character class based on business rules

Definition

filter(s -> s.matches("[aeiouAEIOU]+")) keeps only strings composed entirely of vowels.
  • String.matches(regex) tests whether the entire string matches the pattern, as if anchored with ^ and $.
  • [aeiouAEIOU]+ matches one or more vowels, covering both upper and lower case.
  • Empty strings fail because + demands at least one character.
  • Precompile a Pattern for hot paths to avoid regex recompilation per call.
  • matches vs find: matches requires the whole string to qualify; find matches a substring.

Priya at the National Bank of India filters a list of mnemonic codes and keeps only the ones made entirely of vowels, throwing away any code that contains a consonant, digit, or space.

Understanding the Concepts

What does this question ask?

The interviewer wants you to filter a list of strings, keeping only those made entirely of vowels. This tests regex with matches, filter, and the distinction between matches and find.

How do you filter for vowel-only strings?

JAVA
package com.bank.core;

import java.util.List;

public class VowelOnlyFilter {
    public static void main(String[] args) {
        List<String> codes = List.of(
            "AEI", "SAV", "OUI", "HDFC", "eau", "IO", "");

        List<String> vowelOnly = codes.stream()
            .filter(s -> s.matches("[aeiouAEIOU]+"))
            .toList();

        System.out.println("Vowel-only: " + vowelOnly);
    }
}
TEXT
Vowel-only: [AEI, OUI, eau, IO]

How does matches differ from find?

| Method | Anchoring | Behavior |
|---|---|---|
| matches | Entire string | "SAV" fails because 'S' is not a vowel |
| find | Substring | "SAV" would match 'A' as a vowel substring |

JAVA
import java.util.regex.Pattern;

Pattern vowels = Pattern.compile("[aeiouAEIOU]+");

// matches: whole string must be vowels
System.out.println("SAV".matches("[aeiouAEIOU]+"));           // false
System.out.println(vowels.matcher("SAV").matches());           // false

// find: looks for a vowel substring
System.out.println(vowels.matcher("SAV").find());              // true (matches 'A')

For "only vowels" you need matches, not find.

How do you precompile the Pattern for performance?

JAVA
package com.bank.core;

import java.util.List;
import java.util.regex.Pattern;

public class PrecompiledVowelFilter {
    private static final Pattern VOWEL_ONLY =
        Pattern.compile("[aeiouAEIOU]+");

    public static void main(String[] args) {
        List<String> codes = List.of("AEI", "SAV", "OUI", "HDFC");

        List<String> vowelOnly = codes.stream()
            .filter(s -> VOWEL_ONLY.matcher(s).matches())
            .toList();

        System.out.println(vowelOnly);
    }
}
TEXT
[AEI, OUI]

Precompiling avoids creating a new Pattern on every matches() call, which matters on hot paths.

How do you do this without regex?

JAVA
package com.bank.core;

import java.util.List;
import java.util.Set;

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

    public static void main(String[] args) {
        List<String> codes = List.of("AEI", "SAV", "OUI");

        List<String> vowelOnly = codes.stream()
            .filter(s -> !s.isEmpty() && s.chars()
                .allMatch(c -> VOWELS.contains(
                    Character.toLowerCase((char) c))))
            .toList();

        System.out.println(vowelOnly);
    }
}
TEXT
[AEI, OUI]

This approach uses a Set for O(1) membership checks and allMatch to verify every character is a vowel.

What are the edge cases?

JAVA
System.out.println("".matches("[aeiouAEIOU]+"));     // false (+ needs >= 1)
System.out.println("a".matches("[aeiouAEIOU]+"));    // true
System.out.println("A1".matches("[aeiouAEIOU]+"));   // false (digit)
System.out.println("A E".matches("[aeiouAEIOU]+"));  // false (space)

The + quantifier rejects empty strings, digits, and spaces.

What are common mistakes?

Mistake: using find logic instead of matches.

JAVA
// WRONG: find matches any vowel substring
List<String> wrong = codes.stream()
    .filter(s -> Pattern.compile("[aeiouAEIOU]+")
        .matcher(s).find())
    .toList();
// "SAV" passes because 'A' is found

// CORRECT: matches requires the entire string
List<String> correct = codes.stream()
    .filter(s -> s.matches("[aeiouAEIOU]+"))
    .toList();

Mistake: not handling null strings in the list.

JAVA
// WRONG: NullPointerException if list contains null
// null.matches(...) throws NPE

// CORRECT: filter nulls first
codes.stream()
    .filter(Objects::nonNull)
    .filter(s -> s.matches("[aeiouAEIOU]+"))
    .toList();

What about including accented vowels?

For internationalized applications, the vowel set might include characters like 'e' with accent, 'u' with umlaut, and others.

JAVA
// Extended vowel pattern including common accented vowels
Pattern extendedVowels = Pattern.compile(
    "[aeiouAEIOU\u00E0-\u00F6\u00F8-\u00FC]+");

What are common interview follow-up questions?

  • "How do you filter strings containing only consonants?" Change the character class to [^aeiouAEIOU] but keep + for non-empty.
  • "How do you count vowels in each string?" Use map to pair each string with its vowel count.
  • "How do you make it case-insensitive without listing both cases?" Use Pattern.compile("[aeiou]+", Pattern.CASE_INSENSITIVE).

Visual Overview

DIAGRAM
  List: ["AEI", "SAV", "OUI", "HDFC", "eau", "IO", ""]
       │
       │  .filter(s -> s.matches("[aeiouAEIOU]+"))
       ▼
  ┌──────────────────────────────────────┐
  │  "AEI"  → all vowels → KEEP         │
  │  "SAV"  → 'S' is not a vowel → SKIP │
  │  "OUI"  → all vowels → KEEP         │
  │  "HDFC" → no vowels → SKIP          │
  │  "eau"  → all vowels → KEEP         │
  │  "IO"   → all vowels → KEEP         │
  │  ""     → empty, + fails → SKIP     │
  └──────────────────────────────────────┘
       │
       ▼
  Result: ["AEI", "OUI", "eau", "IO"]

Key Takeaway

Filtering strings to those containing only vowels uses the matches method with a character class of vowels followed by the + quantifier. The critical behavior of matches is that it anchors the pattern to the entire string, so the whole string must be vowels for it to pass. The + quantifier requires at least one character, which correctly rejects empty strings.

For hot paths, precompile the Pattern once and reuse its matcher to avoid regex recompilation on every call. As an alternative, use a Set of vowels with chars().allMatch for a non-regex approach. At the National Bank of India, Priya used this to tag special vowel-only mnemonic codes in a product catalogue.

The most important distinction is matches versus find: matches demands the whole string qualify, while find matches any substring. For "only vowels" you need matches. Decide whether y and accented vowels belong based on business rules.

Remember one sentence: matches anchors to the entire string, so [aeiouAEIOU]+ passes only when every character is a vowel.

---

Share
Use ← → arrow keys to navigate