Back to Stream Practice: String & Character
Q4Question 4 of 11

Find unique words from a sentence using streams

Key Concepts
  • split("\\s+") tokenizes a sentence by one or more whitespace characters into a String array
  • map(String::toLowerCase) normalizes case for case-insensitive uniqueness
  • distinct() removes duplicate words using String's equals/hashCode
  • sorted() produces alphabetical output; LinkedHashSet preserves first-occurrence order
  • collect(toCollection(LinkedHashSet::new)) gives order-preserving uniqueness in one step
  • distinct() uses O(n) memory internally via a HashSet

Definition

Split the sentence by whitespace, normalize to lowercase, apply distinct() for alphabetical unique words, or collect to LinkedHashSet for first-occurrence order.
  • split("\\s+") splits a sentence into words by one or more whitespace characters (spaces, tabs, newlines).
  • map(String::toLowerCase) normalizes case so "NEFT" and "neft" are treated as the same word.
  • distinct() removes duplicates based on String's equals method, using O(n) internal memory.
  • sorted() produces alphabetically ordered output after dedup.
  • toCollection(LinkedHashSet::new) preserves the order words were first seen while removing duplicates.
  • The two approaches differ in output order: sorted gives alphabetical, LinkedHashSet gives first-occurrence order.

At National Bank of India, a transaction narration reads "NEFT transfer to savings NEFT credit from savings." The search index needs unique keywords. Splitting by spaces gives 8 words, but "NEFT" and "savings" appear twice. After dedup, 6 unique keywords remain for the index.

Understanding the Concepts

How do you extract unique words sorted alphabetically?

JAVA
package com.bank.util;

import java.util.*;
import java.util.stream.Collectors;

public class UniqueWordsDemo {
    public static void main(String[] args) {
        String narration = "NEFT transfer to savings NEFT credit from savings";

        // Unique words, sorted alphabetically
        List<String> unique = Arrays.stream(narration.split("\\s+"))
            .map(String::toLowerCase)
            .distinct()
            .sorted()
            .toList();

        System.out.println("Unique (sorted): " + unique);
    }
}
TEXT
Unique (sorted): [credit, from, neft, savings, to, transfer]

How do you preserve first-occurrence order?

JAVA
// Unique words in first-seen order
List<String> ordered = Arrays.stream(narration.split("\\s+"))
    .map(String::toLowerCase)
    .collect(Collectors.toCollection(LinkedHashSet::new))
    .stream()
    .toList();

System.out.println("Unique (first-seen): " + ordered);
TEXT
Unique (first-seen): [neft, transfer, to, savings, credit, from]

How is distinct() different from LinkedHashSet?

| Approach | Output Order | Memory |
|----------|-------------|--------|
| distinct().sorted() | Alphabetical | O(n) for distinct + O(n) for sort |
| distinct() alone | Encounter order (source order) | O(n) |
| toCollection(LinkedHashSet::new) | First-occurrence order | O(k) where k = distinct count |

distinct() alone already preserves encounter order in ordered streams. Adding sorted() reorders alphabetically. The LinkedHashSet approach is equivalent to distinct() for ordered streams but collects into a Set for downstream use.

What about punctuation?

JAVA
// Handle punctuation: split by non-word characters
String withPunctuation = "Account-holder: Rajesh, Priya. Account-holder: Amit.";

List<String> words = Arrays.stream(withPunctuation.split("[^a-zA-Z]+"))
    .map(String::toLowerCase)
    .filter(s -> !s.isEmpty())
    .distinct()
    .toList();
// [account, holder, rajesh, priya, amit]

What are common mistakes?

WRONG: Splitting by a single space instead of \\s+.

JAVA
// WRONG: split(" ") fails on multiple spaces or tabs
String messy = "NEFT  transfer\tto  savings";
String[] wrong = messy.split(" ");  // produces empty strings!

CORRECT: Use \\s+ to handle any whitespace.

JAVA
// CORRECT: \\s+ handles multiple spaces, tabs, newlines
String[] correct = messy.split("\\s+");

What should a senior engineer know?

  • distinct() uses an internal HashSet that grows with the number of distinct elements. For very large text processing, consider streaming the text lazily with BufferedReader.lines() instead of loading it all into memory.
  • For locale-aware case normalization, use toLowerCase(Locale.ROOT) to avoid the Turkish-i problem where "I".toLowerCase() produces a dotless i in Turkish locale.
  • The split method compiles the regex each time. For repeated calls, pre-compile with Pattern.compile("\\s+").splitAsStream(sentence) for better performance.

Visual Overview

DIAGRAM
  "NEFT transfer to savings NEFT credit from savings"
       │
       ▼
  split("\\s+")
  [NEFT, transfer, to, savings, NEFT, credit, from, savings]
       │
       ▼
  map(toLowerCase)
  [neft, transfer, to, savings, neft, credit, from, savings]
       │
       ▼
  distinct()
  [neft, transfer, to, savings, credit, from]  (6 unique)
       │
       ▼
  sorted()
  [credit, from, neft, savings, to, transfer]  (alphabetical)

Key Takeaway

Extracting unique words from a sentence uses split("\\s+") to tokenize, map(String::toLowerCase) for case-insensitive dedup, distinct() to remove duplicates, and sorted() or LinkedHashSet for ordering. The distinct() approach uses O(n) memory internally.

At National Bank of India, a narration indexer processes transaction descriptions. Eight words become six unique keywords for the search index. The LinkedHashSet approach preserves first-occurrence order (neft first because it appeared first), while sorted() gives alphabetical order.

Remember one sentence: split by whitespace, normalize case, then distinct gives unique words, with sorted or LinkedHashSet controlling the output order.

---

Share
Use ← → arrow keys to navigate