Definition
- chars() returns IntStream of char values as int.
- filter(!isWhitespace) keeps only non-whitespace characters.
- mapToObj(c -> String.valueOf((char) c)) converts each code point to a one-character string.
- Collectors.joining() concatenates with no separator, rebuilding a whitespace-free string.
- Character.isWhitespace is Unicode-aware and broader than just the space character.
Priya at the National Bank of India receives account codes with accidental spaces and tabs like SAV 2001, and squeezes out every blank to produce SAV2001 for storage and validation.
Understanding the Concepts
What does this question ask?
The interviewer wants you to strip all whitespace from a string using the Stream API. This tests chars(), filtering by character classification, and the standard filter-map-join idiom for rebuilding strings.
How do you remove whitespace with streams?
package com.bank.core;
import java.util.stream.Collectors;
public class RemoveWhitespace {
public static void main(String[] args) {
String raw = "SAV 2001\tHDFC \n";
String cleaned = raw.chars()
.filter(c -> !Character.isWhitespace(c))
.mapToObj(c -> String.valueOf((char) c))
.collect(Collectors.joining());
System.out.println("Raw: '" + raw.strip() + "'");
System.out.println("Cleaned: '" + cleaned + "'");
}
}
Raw: 'SAV 2001 HDFC'
Cleaned: 'SAV2001HDFC'
What does Character.isWhitespace cover?
| Character | isWhitespace | Description |
|---|---|---|
| ' ' (space) | true | Regular space |
| '\t' (tab) | true | Horizontal tab |
| '\n' (newline) | true | Line feed |
| '\r' (return) | true | Carriage return |
| '\f' (form feed) | true | Form feed |
| Unicode spaces | true | Non-breaking space, etc. |
This is broader than checking for c == ' ' and covers all Unicode whitespace categories.
How is the stream approach different from replaceAll?
// Regex approach: shorter but limited to regex patterns
String regex = raw.replaceAll("\\s+", "");
// Stream approach: accepts any predicate
String stream = raw.chars()
.filter(c -> !Character.isWhitespace(c))
.mapToObj(c -> String.valueOf((char) c))
.collect(Collectors.joining());
| Feature | replaceAll | Stream filter |
|---|---|---|
| Brevity | Shorter | More verbose |
| Flexibility | Regex patterns only | Any predicate |
| Readability | Familiar to most | Explicit step-by-step |
| Performance | Compiles regex each call | No regex overhead |
The stream version shines when the filtering rule is more complex than whitespace, such as keeping only digits and uppercase letters.
How do you keep only specific character types?
package com.bank.core;
import java.util.stream.Collectors;
public class KeepAlphanumeric {
public static void main(String[] args) {
String input = "SAV-2001 (HDFC) #Active";
// Keep only letters and digits
String alphanumeric = input.chars()
.filter(c -> Character.isLetterOrDigit(c))
.mapToObj(c -> String.valueOf((char) c))
.collect(Collectors.joining());
System.out.println("Alphanumeric: " + alphanumeric);
}
}
Alphanumeric: SAV2001HDFCActive
What are common mistakes?
Mistake 1: forgetting to map to String before joining.
// WRONG: cannot join an IntStream directly
// raw.chars().filter(...).collect(Collectors.joining());
// IntStream does not have collect(Collector)
// CORRECT: map to String objects first
raw.chars()
.filter(c -> !Character.isWhitespace(c))
.mapToObj(c -> String.valueOf((char) c))
.collect(Collectors.joining());
Mistake 2: checking only for space character instead of all whitespace.
// WRONG: misses tabs, newlines, Unicode spaces
.filter(c -> c != ' ')
// CORRECT: covers all whitespace types
.filter(c -> !Character.isWhitespace(c))
What about building the result with StringBuilder instead?
String result = raw.chars()
.filter(c -> !Character.isWhitespace(c))
.collect(
StringBuilder::new,
(sb, c) -> sb.append((char) c),
StringBuilder::append)
.toString();
This avoids creating intermediate one-character strings but is more verbose. Both approaches produce the same result.
What about using codePoints() for Unicode safety?
For strings with characters outside the Basic Multilingual Plane, use codePoints() and Character.toString(int) instead of casting to char.
String unicodeSafe = raw.codePoints()
.filter(cp -> !Character.isWhitespace(cp))
.mapToObj(Character::toString)
.collect(Collectors.joining());
What are common interview follow-up questions?
- "How do you remove only leading and trailing whitespace?" Use
strip()(Java 11+) ortrim(). - "How do you collapse multiple spaces into one?" Use
replaceAll("\\s+", " "). - "How do you remove specific characters?" Change the filter predicate to exclude those characters.
Visual Overview
String: "SAV 2001\tHDFC \n"
│
│ .chars() → IntStream [83, 65, 86, 32, 50, ...]
│
│ .filter(!isWhitespace)
▼
IntStream [83, 65, 86, 50, 48, 48, 49, 72, 68, 70, 67]
│ S A V 2 0 0 1 H D F C
│
│ .mapToObj(c -> String.valueOf((char) c))
▼
Stream<String> ["S","A","V","2","0","0","1","H","D","F","C"]
│
│ .collect(Collectors.joining())
▼
"SAV2001HDFC"
Key Takeaway
To strip all whitespace with streams, take the characters with chars(), filter out whitespace using the negation of Character.isWhitespace, map each surviving code point to a one-character string, and join with the no-argument joining to rebuild a clean string. For a code like "SAV 2001 HDFC", this drops all spaces and tabs to produce SAV2001HDFC.
Character.isWhitespace is Unicode-aware and catches far more than the plain space character: tabs, newlines, and other whitespace forms. This matters for real input at the National Bank of India where account codes arrive with accidental spacing before validation and storage.
The stream approach earns its keep when the filtering rule is more complex than whitespace, because you just swap the predicate and the same filter-map-join shape rebuilds the sanitized string. For the specific case of whitespace alone, the concise regex replaceAll is often clearer.
Remember one sentence: chars(), filter(!isWhitespace), mapToObj to String, and joining() is the standard stream idiom for stripping characters and rebuilding a cleaned string.
---