Definition
- Identity: empty string
"", the starting accumulator and the empty-stream result. - Accumulator: if running string is empty, just the name; otherwise
running + ", " + name. - Combiner: joins two partial strings for parallel execution with the same separator logic.
- O(n^2): string concatenation in reduce copies all previous characters each time.
- Production alternative:
Collectors.joining(", ")uses StringBuilder for O(n) performance.
Priya is writing one line listing everyone on the team: "Rajesh, Priya, Amit". She starts with an empty line, adds the first name, and for each later name adds a comma-space then the name.
Understanding the Concepts
How do you concatenate names with reduce?
package com.bank.hr;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
public class ReduceConcat {
public static void main(String[] args) {
List<Employee> staff = List.of(
new Employee("Rajesh", "Loans", new BigDecimal("95000")),
new Employee("Priya", "IT", new BigDecimal("120000")),
new Employee("Amit", "Finance", new BigDecimal("85000")));
String names = staff.stream()
.map(Employee::getName)
.reduce("", (a, b) -> a.isEmpty() ? b : a + ", " + b);
System.out.println(names);
}
}
Rajesh, Priya, Amit
How does the fold work step by step?
| Step | Running string | Next name | Result |
|---|---|---|---|
| Start | "" | - | "" |
| 1 | "" | "Rajesh" | "Rajesh" (empty check) |
| 2 | "Rajesh" | "Priya" | "Rajesh, Priya" |
| 3 | "Rajesh, Priya" | "Amit" | "Rajesh, Priya, Amit" |
What is the combiner for?
The combiner merges two partial strings when the stream runs in parallel.
String names = staff.stream()
.map(Employee::getName)
.reduce("",
(a, b) -> a.isEmpty() ? b : a + ", " + b,
(s1, s2) -> s1.isEmpty() ? s2 : (s2.isEmpty() ? s1 : s1 + ", " + s2));
Why is this O(n^2)?
Each + creates a new String object, copying all previous characters. For n names, the total work is 1 + 2 + 3 + ... + n = O(n^2).
What are common mistakes?
Mistake: not guarding the first element, producing a leading comma.
// WRONG: leading comma before the first name
String wrong = staff.stream()
.map(Employee::getName)
.reduce("", (a, b) -> a + ", " + b);
// ", Rajesh, Priya, Amit" ← leading comma!
// CORRECT: check if running string is empty
String correct = staff.stream()
.map(Employee::getName)
.reduce("", (a, b) -> a.isEmpty() ? b : a + ", " + b);
// "Rajesh, Priya, Amit"
What is the production alternative?
// O(n) with StringBuilder internally
String names = staff.stream()
.map(Employee::getName)
.collect(Collectors.joining(", "));
Collectors.joining is both simpler and more efficient. The reduce approach is for demonstrating the fold.
What about an empty stream?
With the identity "", an empty stream returns "", which is a correct empty result.
What should a senior engineer know?
- Never use reduce with string concatenation in production; use
Collectors.joining. - The reduce approach demonstrates folding mechanics, which is why interviews ask for it.
- The combiner must handle empty partials correctly for parallel correctness.
- In Java 11+,
String.isBlank()is available butisEmpty()is the correct check here (we want non-whitespace check).
What are common interview follow-up questions?
- "Why not use reduce for string concatenation in production?" O(n^2) performance.
- "What does Collectors.joining use internally?" StringBuilder via StringJoiner.
- "What happens with an empty stream?" Returns the empty string identity.
Visual Overview
reduce("", (a, b) -> a.isEmpty() ? b : a + ", " + b)
Step 1: "" + "Rajesh" → "Rajesh" (empty check)
Step 2: "Rajesh" + "Priya" → "Rajesh, Priya"
Step 3: "Rajesh, Priya" + "Amit" → "Rajesh, Priya, Amit"
Result: "Rajesh, Priya, Amit"
Production alternative:
Collectors.joining(", ") ← O(n) with StringBuilder
Key Takeaway
To concatenate names with reduce I map each employee to its name, then fold starting from an empty string. The accumulator checks if the running string is empty to avoid a leading comma, then appends the separator and the next name.
The combiner must also handle empty partials for parallel correctness. The result for an empty stream is the empty string identity.
The critical point is that string concatenation in reduce is O(n^2). In production I always use Collectors.joining(", "), which uses a StringBuilder for O(n) performance. The reduce form demonstrates the folding mechanics, which is what the interview tests.
Remember one sentence: reduce can concatenate strings but is O(n^2); use Collectors.joining in production for O(n) StringBuilder-backed concatenation.
---