Count Words in a String in Java
Problem
Words in a sentence are typically separated by whitespace, so counting them means counting how many whitespace-separated tokens the string splits into.
Given a sentence, count how many words it contains.
Java Program
public class CountWords {
public static void main(String[] args) {
String str = "The quick brown fox";
String trimmed = str.trim();
// Splitting on a run of whitespace avoids counting empty tokens between words
String[] words = trimmed.isEmpty() ? new String[0] : trimmed.split("\\s+");
System.out.println("Words: " + words.length);
}
}Output
Core Logic
Trimming the sentence and splitting on runs of whitespace turns the word count into the length of the resulting array.
- 1
str.trim()removes any leading or trailing whitespace, so a stray space doesn't create an empty extra token. - 2
trimmed.split("\\s+")splits on one or more whitespace characters, so multiple spaces between words don't create empty tokens either. - 3The number of elements in the resulting array is exactly the number of words.
- 4An empty trimmed string is handled as a special case, since splitting it would otherwise report one word instead of zero.
"The quick brown fox", splitting on whitespace produces ["The", "quick", "brown", "fox"] — four words.Key Point: Splitting on " " alone (a single space) would count an empty string between two consecutive spaces as its own word — "\\s+" collapses any run of whitespace into a single split point.
Why: split() has to scan the whole string once to find whitespace boundaries, and it allocates a new String for every word found.
Key Concepts
Approach 2: Manual Whitespace Scan
public class CountWordsManual {
public static void main(String[] args) {
String str = "The quick brown fox";
int count = 0;
boolean inWord = false;
// Count only the transitions from whitespace into the start of a word
for (char c : str.toCharArray()) {
if (!Character.isWhitespace(c)) {
if (!inWord) count++;
inWord = true;
} else {
inWord = false;
}
}
System.out.println("Words: " + count);
}
}
Output
Core Logic
Instead of building an array of words, a single pass can just count the transitions from whitespace into a word.
- 1A boolean
inWordflag tracks whether the scan is currently inside a word. - 2
Character.isWhitespace(c)checks each character; a non-whitespace character starting a new word incrementscount. - 3
inWordis set totruewhile inside a word and reset tofalseat the first whitespace character after it. - 4Consecutive whitespace characters don't trigger extra increments, since
countonly goes up on the transition into a word.
"The quick brown fox" character by character increments count at 'T', 'q', 'b', and 'f' — the start of each word.Key Point: This never allocates an array of the individual words, so for very long text it uses less memory than the split-based version while still running in a single pass.
Why: The scan tracks word boundaries with a single boolean flag, never allocating an array of the individual words.
Key Concepts
Approach 3: Java 8
import java.util.Arrays;
public class CountWordsStream {
public static void main(String[] args) {
String str = "The quick brown fox";
// filter() drops empty tokens, covering the empty-string edge case in one step
long count = Arrays.stream(str.trim().split("\\s+"))
.filter(w -> !w.isEmpty())
.count();
System.out.println("Words: " + count);
}
}
Output
Core Logic
Streaming the split tokens and filtering out any empty ones counts the words in a single expression, without a separate empty-string special case.
- 1
str.trim().split("\\s+")splits the trimmed string the same way the primary approach does. - 2
Arrays.stream(...)turns the resulting array into aStream<String>. - 3
.filter(w -> !w.isEmpty())drops any empty token, which covers the case where the trimmed string was empty to begin with —split()on an empty string returns an array containing one empty token, not zero. - 4
.count()returns how many tokens survived the filter, as along.
"The quick brown fox", streaming ["The", "quick", "brown", "fox"] and filtering out empties (there are none here) still counts 4.Key Point: The filter() step replaces the primary approach's explicit trimmed.isEmpty() ? new String[0] : ... check with a single condition that works whether or not the trimmed string was empty.
Why: split() still scans the whole string once and allocates a token array, which the stream then wraps and filters.