Introduction
Counting the occurrences of each word in a String is a common Java programming task. The goal is to find every unique word in a sentence and determine how many times it appears.
For example, consider the sentence "Java is easy and Java is powerful". The word "Java" appears twice, "is" appears twice, and the remaining words appear once. A Java program can calculate these frequencies and display the results.
Word frequency counting is useful in text analysis, document processing, search applications, and log analysis. It is also a popular Java coding interview problem because it involves Strings, collections, loops, and data processing.
In this tutorial, I will explain how to count the occurrences of each word in a String using a Map and the Java Stream API. I will also show how to handle uppercase letters, punctuation, and whitespace. Finally, you will learn how to display words in order of frequency.
1. Understanding the Word Frequency Problem
Consider the following String:
Java is easy Java is powerful Java is popular
The program should identify each unique word and count how many times it appears.
| Word | Occurrences |
|---|---|
| Java | 3 |
| is | 3 |
| easy | 1 |
| powerful | 1 |
| popular | 1 |
The expected output is:
Java = 3
is = 3
easy = 1
powerful = 1
popular = 1
To solve this problem, the program needs to perform three operations:
- Split the input String into individual words.
- Count how many times each word appears.
- Display each word with its frequency.
2. Count Word Occurrences in Java Using HashMap
A HashMap is one of the most practical ways to count word occurrences in Java. It stores data as key-value pairs. For this problem, the key represents a word, and the value represents its frequency.
For example, when the program encounters the word "Java" for the first time, it stores the entry Java = 1. When the same word appears again, the count increases to 2.
In the following example, I will use LinkedHashMap, which extends HashMap. It preserves insertion order, so the words appear in the same order as their first occurrence in the input.
Java Program Using LinkedHashMap
import java.util.LinkedHashMap;
import java.util.Map;
public class WordFrequencyUsingMap {
public static void main(String[] args) {
String input = "Java is easy Java is powerful Java is popular";
// Split the sentence into words using whitespace.
String[] words = input.split("\\s+");
Map<String, Integer> wordCounts = new LinkedHashMap<>();
// Count how many times each word appears.
for (String word : words) {
wordCounts.merge(word, 1, Integer::sum);
}
System.out.println("Word occurrences:");
// Display each word and its frequency.
wordCounts.forEach((word, count) ->
System.out.println(word + " = " + count)
);
}
}
Output
Word occurrences:
Java = 3
is = 3
easy = 1
powerful = 1
popular = 1
How Does the Program Work?
Let's understand each important part of the program.
Step 1: Split the String into Words
String[] words = input.split("\\s+");
The split() method divides a String into an array based on a regular expression.
Here, \\s+ is the Java String representation of the regular expression \s+. It matches one or more whitespace characters, such as spaces and tabs.
For the given input, the resulting array contains:
["Java", "is", "easy", "Java", "is",
"powerful", "Java", "is", "popular"]
Using \\s+ is more flexible than splitting on a single space because it can handle multiple consecutive spaces.
Step 2: Create a Map to Store Word Frequencies
Map<String, Integer> wordCounts = new LinkedHashMap<>();
The map stores each unique word and its number of occurrences.
Stringrepresents the word.Integerrepresents its frequency.LinkedHashMapmaintains the insertion order of the words.
Step 3: Count the Occurrences of Each Word
for (String word : words) {
wordCounts.merge(word, 1, Integer::sum);
}
The enhanced for loop processes each word in the array.
The merge() method performs two operations:
- If the word does not exist in the map, it inserts the word with a count of
1. - If the word already exists, it increases the existing count by
1.
For example, when the word "Java" appears three times, its stored count changes from 1 to 2 and then to 3.
This eliminates the need to write separate conditions using containsKey() or get().
Step 4: Display the Word Frequencies
wordCounts.forEach((word, count) ->
System.out.println(word + " = " + count)
);
The forEach() method processes every entry in the map and prints the word with its frequency.
When should you use this approach?
This approach is ideal for beginners and coding interviews. It is easy to understand and efficient for counting words in a String.
Keep in mind that the example treats uppercase and lowercase words as different values. It also keeps punctuation attached to words. The next example addresses these issues.
3. Count Word Occurrences Ignoring Case and Punctuation
Real-world text usually contains uppercase letters, punctuation, and different types of whitespace.
Consider the following sentence:
Java is easy. Java is powerful, and JAVA is popular!
A basic whitespace-based split would treat "easy." and "powerful," as words that include punctuation. It would also treat "Java" and "JAVA" as different words.
For general word frequency analysis, it is often better to ignore case and separate words from common punctuation marks.
The following program converts the text to lowercase and uses a regular expression to split it into words.
Java Program
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
public class CaseInsensitiveWordFrequency {
// Treat sequences of non-letter and non-digit characters as separators.
private static final Pattern WORD_SEPARATOR =
Pattern.compile("[^\\p{L}\\p{N}]+");
public static void main(String[] args) {
String input =
"Java is easy. Java is powerful, and JAVA is popular!";
// Normalize uppercase and lowercase letters.
String normalizedInput = input.toLowerCase(Locale.ROOT);
Map<String, Integer> wordCounts = new LinkedHashMap<>();
// Split the text into words and ignore empty values.
for (String word : WORD_SEPARATOR.split(normalizedInput)) {
if (word.isEmpty()) {
continue;
}
wordCounts.merge(word, 1, Integer::sum);
}
System.out.println("Word occurrences:");
wordCounts.forEach((word, count) ->
System.out.println(word + " = " + count)
);
}
}
Output
Word occurrences:
java = 3
is = 3
easy = 1
powerful = 1
and = 1
popular = 1
Explanation
1. Convert the String to Lowercase
String normalizedInput = input.toLowerCase(Locale.ROOT);
The toLowerCase() method converts uppercase characters to lowercase.
Using Locale.ROOT makes the conversion independent of the system's default locale. This provides more predictable behavior for general-purpose text processing.
As a result, "Java", "JAVA", and "java" are all counted under the same key.
2. Separate Words from Punctuation
Pattern.compile("[^\\p{L}\\p{N}]+");
This regular expression matches one or more characters that are neither Unicode letters nor Unicode digits.
\p{L}matches Unicode letters.\p{N}matches Unicode numbers.[^...]matches characters outside the specified groups.+matches one or more consecutive separator characters.
The program uses the compiled pattern to split the sentence into individual words. Punctuation and whitespace act as separators.
3. Skip Empty Words
if (word.isEmpty()) {
continue;
}
This check prevents empty values from being added to the map. Empty values can occur when the input starts with punctuation or contains only separators.
4. Count Each Word
wordCounts.merge(word, 1, Integer::sum);
The map stores each normalized word and updates its frequency.
When should you use this approach?
This approach is useful for simple text analysis, keyword counting, and processing sentences entered by users.
However, it is a simplified definition of a word. For example, it splits contractions such as "don't" into "don" and "t". It also separates hyphenated words and certain Unicode text sequences.
If your application needs language-specific word boundaries, consider a dedicated text-processing or natural language processing solution.
4. Count Word Occurrences Using Java Streams
The Java Stream API provides another way to count words in a String.
Instead of using an explicit loop, you can split the input into words, process them through a Stream, and collect the results into a Map.
This approach is useful when you are already familiar with Java Streams and want to apply grouping and counting operations.
Java Program Using Stream API
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class WordFrequencyUsingStreams {
private static final Pattern WORD_SEPARATOR =
Pattern.compile("[^\\p{L}\\p{N}]+");
public static void main(String[] args) {
String input =
"Java is easy. Java is powerful, and JAVA is popular!";
// Normalize the text before counting words.
String normalizedInput = input.toLowerCase(Locale.ROOT);
// Group identical words and count their occurrences.
Map<String, Long> wordCounts =
WORD_SEPARATOR.splitAsStream(normalizedInput)
.filter(word -> !word.isEmpty())
.collect(Collectors.groupingBy(
Function.identity(),
LinkedHashMap::new,
Collectors.counting()
));
System.out.println("Word occurrences:");
wordCounts.forEach((word, count) ->
System.out.println(word + " = " + count)
);
}
}
Output
Word occurrences:
java = 3
is = 3
easy = 1
powerful = 1
and = 1
popular = 1
How Does the Stream API Solution Work?
Let's examine the main operations.
Step 1: Create a Stream of Words
WORD_SEPARATOR.splitAsStream(normalizedInput)
The splitAsStream() method splits the input using the compiled regular expression and returns a Stream of Strings.
Unlike String.split(), which creates an array, this method lets you process the resulting words directly through a Stream pipeline.
Step 2: Remove Empty Words
.filter(word -> !word.isEmpty())
The filter() operation removes empty Strings from the Stream.
Step 3: Group and Count Words
.collect(Collectors.groupingBy(
Function.identity(),
LinkedHashMap::new,
Collectors.counting()
));
The groupingBy() collector organizes identical words into groups.
Function.identity()uses each word as its grouping key.LinkedHashMap::newcreates a map that preserves the insertion order of words.Collectors.counting()counts the elements in each group.
Notice that Collectors.counting() produces values of type Long. Therefore, the result uses Map<String, Long> instead of Map<String, Integer>.
Finally, the program prints every word and its count.
When should you use this approach?
Use this approach when you want to practice the Stream API or integrate word counting into an existing Stream-based processing pipeline.
For straightforward word counting, a traditional loop with a Map is usually easier for beginners to follow.
5. Find the Most Frequently Occurring Words in Java
Sometimes counting words is only the first step. You may also want to identify which words appear most often.
For example, a text analysis application might display the most common words in a document. You can solve this problem by counting word frequencies and sorting the results in descending order.
The following example uses LinkedHashMap to count words and the Stream API to sort the results by frequency.
Java Program to Sort Words by Frequency
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
public class MostFrequentWords {
private static final Pattern WORD_SEPARATOR =
Pattern.compile("[^\\p{L}\\p{N}]+");
public static void main(String[] args) {
String input = "Java Python Java Spring Python Java Spring";
String normalizedInput = input.toLowerCase(Locale.ROOT);
Map<String, Integer> wordCounts = new LinkedHashMap<>();
// Count the occurrences of each word.
for (String word : WORD_SEPARATOR.split(normalizedInput)) {
if (!word.isEmpty()) {
wordCounts.merge(word, 1, Integer::sum);
}
}
System.out.println("Words sorted by frequency:");
// Sort by frequency (highest first), then alphabetically.
wordCounts.entrySet().stream()
.sorted(
Map.Entry.<String, Integer>comparingByValue(
Comparator.reverseOrder()
).thenComparing(Map.Entry.comparingByKey())
)
.forEach(entry ->
System.out.println(
entry.getKey() + " = " + entry.getValue()
)
);
}
}
Output
Words sorted by frequency:
java = 3
python = 2
spring = 2
Explanation
The program first counts each word using a LinkedHashMap.
It then converts the map entries into a Stream:
wordCounts.entrySet().stream()
The sorted() method arranges the entries according to their frequencies.
Comparator.reverseOrder() sorts the frequencies in descending order, placing the most frequently occurring words first.
The additional thenComparing() operation sorts words alphabetically when two words have the same frequency.
For example, both "python" and "spring" appear twice. The program displays "python" first because it comes before "spring" alphabetically.
This approach is useful for finding the most common words in documents, generating keyword frequency reports, and performing basic text analysis.
6. How to Run the Java Programs
You can run these examples in IntelliJ IDEA, Eclipse, VS Code, or directly from the command line.
You need a Java Development Kit (JDK). Java 17 or later is sufficient for all examples in this tutorial.
Step 1: Create a Java File
Create a file named WordFrequencyUsingMap.java and copy the complete code from the first example.
The filename must match the public class name.
Step 2: Compile the Program
Open a terminal in the directory containing the Java file and run:
javac WordFrequencyUsingMap.java
Step 3: Run the Program
java WordFrequencyUsingMap
The program displays each unique word with its number of occurrences.
You can run the remaining examples in the same way. Save each program in a separate Java file whose name matches its public class.
7. Comparing Word Frequency Counting Approaches
Both Map-based and Stream-based solutions are suitable for counting word occurrences. The following table summarizes their differences.
| Approach | Advantages | Best Use Case |
|---|---|---|
| LinkedHashMap with Loop | Simple, readable, and efficient | Beginners and coding interviews |
| Map with Text Normalization | Handles common punctuation and case differences | Basic real-world text analysis |
| Java Streams | Supports declarative grouping and counting | Stream-based data processing |
| Frequency Sorting | Identifies the most common words | Keyword frequency reports |
Time and Space Complexity
For Map-based word counting, each word is processed once.
- Average counting time: O(n) map operations, where n is the number of words.
- Map space: O(k), where k is the number of distinct words.
- Sorting time: O(k log k) when sorting the unique words by frequency.
These estimates treat individual map operations as average constant-time operations. They do not include the cost of scanning, creating, or hashing the word Strings.
For ordinary text processing, both the loop-based and Stream-based solutions perform well.
8. Common Mistakes When Counting Words in Java
1. Splitting Only on a Single Space
Using split(" ") may produce empty Strings when the input contains multiple consecutive spaces.
For simple whitespace-separated text, use:
String[] words = input.split("\\s+");
For text containing punctuation, consider a more suitable regular expression.
2. Ignoring Case Sensitivity
Java treats "Java" and "java" as different Strings.
If the application needs case-insensitive counting, normalize the text before processing it.
3. Counting Punctuation as Part of Words
When you split only on whitespace, "Java" and "Java." become different keys.
Use an appropriate tokenizer or regular expression when punctuation should not affect word frequency.
4. Assuming HashMap Preserves Word Order
A regular HashMap does not guarantee iteration order.
Use LinkedHashMap when you want to display words in the order they first appear.
5. Forgetting About Empty Input
An empty or whitespace-only String contains no words.
A reusable word-counting method should return an empty map for such input instead of counting an empty String as a word.
Also, decide how to handle null. One option is to reject it with a clear exception:
if (input == null) {
throw new IllegalArgumentException("Input cannot be null");
}
6. Assuming Every Sequence of Letters Is a Linguistic Word
Regular expressions work well for simple examples, but real languages have more complex word boundaries.
Contractions, hyphenated words, abbreviations, and languages without spaces between words may need specialized tokenization.
Choose a word-splitting strategy that matches the needs of your application.
9. Frequently Asked Questions
How do you count occurrences of each word in a Java String?
Split the String into words and store each word as a key in a Map. Increase the corresponding value whenever the word appears again. Finally, iterate over the map to display each word and its count.
Which Java collection is best for counting word frequency?
A HashMap is a common choice because it provides efficient average-time lookup and updates. Use LinkedHashMap when the order of first occurrence matters.
Can I count word occurrences using Java Streams?
Yes. You can use Pattern.splitAsStream() to create a Stream of words and Collectors.groupingBy() with Collectors.counting() to calculate their frequencies.
How can I count words without considering uppercase and lowercase letters?
Convert the input to lowercase using toLowerCase(Locale.ROOT) before counting. This works well for basic case-insensitive word frequency analysis.
How do I find the most repeated word in a String?
Count the occurrences of each word using a Map. Then find the entry with the highest frequency. You can use a loop, Collections.max(), or Stream operations to identify the most frequent word.
What is the difference between counting words and counting word occurrences?
Counting words gives the total number of words in a sentence. Counting word occurrences determines how often each unique word appears.
For example, the sentence "Java is Java" contains three words but only two unique words. The word "Java" appears twice, while "is" appears once.
Conclusion
In this tutorial, you learned how to count the occurrences of each word in a Java String using maps and the Java Stream API.
The Map-based approach is a good choice for most applications. It is simple to understand and provides efficient word frequency counting. Using LinkedHashMap also makes the output predictable by preserving the order in which words first appear.
You also learned how to normalize text so that differences in capitalization and common punctuation marks do not affect the results. The Stream API provides another useful approach for developers who prefer declarative data processing.
For more advanced text analysis, you can sort words by frequency to identify the most commonly used terms in a document.
As a next step, try modifying the program to find only the repeated words or to display the five most frequently occurring words in a paragraph. Both exercises build on the same Java collection and String-processing concepts.
Continue Learning with Java Guides
Want to take your programming skills to the next level? Explore my practical Udemy courses on Java, Spring Boot, Microservices, Full-Stack Development, Docker, and Generative AI. You can also follow my structured learning roadmaps to become a professional developer.
By Ramesh Fadatare - Udemy Instructor Partner
Comments
Post a Comment
Leave Comment