TS
JV

TypeScript to Java

10 lessons

Progress0%
1Introduction: Compiler to JVM2Type Systems: Structural vs Nominal3Classes & OOP4Generics5Modules to Packages6Null Safety7Async to Threads8Ecosystem9Exception Handling10Collections and Stream API
All Mirror Courses
TS
JV
Collections and Stream API
MirrorLesson 10 of 10
Lesson 10

Collections and Stream API

Java Stream API vs TypeScript array methods — map/filter/reduce/groupBy

Introduction

In this lesson, you'll learn about collections and stream api in Java. Coming from TypeScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
TS
From TypeScript:

In TypeScript, you're familiar with java stream api vs typescript array methods — map/filter/reduce/groupby.

JV
In Java:

Java has its own approach to java stream api vs typescript array methods — map/filter/reduce/groupby, which we'll explore step by step.

The Java Way

Let's see how Java handles this concept. Here's a typical example:

JV
Java Example
import java.util.*;
import java.util.stream.*;
import static java.util.stream.Collectors.*;

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6);

// map / filter / reduce
List<Integer> doubled = numbers.stream()
    .map(x -> x * 2)
    .collect(toList());                    // [2,4,6,8,10,12]

List<Integer> evens = numbers.stream()
    .filter(x -> x % 2 == 0)
    .collect(toList());                    // [2,4,6]

int sum = numbers.stream()
    .reduce(0, Integer::sum);             // 21

// Chaining
int result = numbers.stream()
    .filter(x -> x > 2)
    .mapToInt(x -> x * x)
    .sum();                               // 86

// findFirst / anyMatch / allMatch
Optional<Integer> first = numbers.stream().filter(x -> x > 3).findFirst();
boolean any = numbers.stream().anyMatch(x -> x > 5);
boolean all = numbers.stream().allMatch(x -> x > 0);

// flatMap
List<List<Integer>> nested = List.of(List.of(1,2), List.of(3,4));
List<Integer> flat = nested.stream()
    .flatMap(Collection::stream)
    .collect(toList());

// groupingBy (like lodash groupBy)
Map<String, List<Integer>> grouped = numbers.stream()
    .collect(groupingBy(n -> n % 2 == 0 ? "even" : "odd"));

Comparing to TypeScript

Here's how you might have written similar code in TypeScript:

TS
TypeScript (What you know)
const numbers = [1, 2, 3, 4, 5, 6];

// map / filter / reduce
const doubled = numbers.map(x => x * 2);      // [2,4,6,8,10,12]
const evens   = numbers.filter(x => x % 2 === 0); // [2,4,6]
const sum     = numbers.reduce((acc, x) => acc + x, 0); // 21

// Chaining
const result = numbers
  .filter(x => x > 2)
  .map(x => x * x)
  .reduce((acc, x) => acc + x, 0); // 9+16+25+36 = 86

// find / some / every
const first = numbers.find(x => x > 3);   // 4
const any   = numbers.some(x => x > 5);   // true
const all   = numbers.every(x => x > 0);  // true

// flatMap
const nested = [[1,2],[3,4]];
const flat = nested.flatMap(x => x);     // [1,2,3,4]

// groupBy (ES2024 / lodash)
const grouped = Object.groupBy(numbers, n => n % 2 === 0 ? "even" : "odd");
Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

Java streams are lazy — operations run only when a terminal op (collect/sum/findFirst) is called

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

stream() creates a stream; collect(toList()) materializes it back to a List

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

Optional<T> instead of undefined for findFirst/findAny

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

Collectors.groupingBy replaces lodash groupBy or Object.groupBy

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

JV
In Java:

mapToInt/mapToLong/mapToDouble return primitive streams with sum()/avg()/max()

Step-by-Step Breakdown

1. Stream Pipeline

Java streams are lazy pipelines: source → intermediate ops (map/filter) → terminal op (collect/sum). The chain doesn't run until terminal op.

TS
TypeScript
numbers.filter(x => x > 2).map(x => x * 2)
JV
Java
numbers.stream()      // create stream
    .filter(x -> x > 2)  // intermediate (lazy)
    .map(x -> x * 2)     // intermediate (lazy)
    .collect(toList());  // terminal — triggers evaluation

2. Primitive Streams

mapToInt/Long/Double return specialized streams with numeric terminals (sum, average, stats) — avoids Integer boxing.

TS
TypeScript
numbers.map(x => x * x).reduce((a,b) => a + b, 0)
JV
Java
numbers.stream()
    .mapToInt(x -> x * x)  // IntStream (unboxed)
    .sum();                // IntStream.sum()

3. Collectors

Collectors are the terminal ops that materialize streams. joining, groupingBy, counting, toMap are the most useful.

TS
TypeScript
items.map(x=>x.name).join(", ")
Object.groupBy(items, x=>x.category)
JV
Java
// join strings
stream.collect(joining(", "))
// group by key
stream.collect(groupingBy(Item::getCategory))
// count per group
stream.collect(groupingBy(Item::getCategory, counting()))

4. Optional from Streams

findFirst/findAny return Optional<T>. Use orElse, orElseGet, or map to handle the absent case.

TS
TypeScript
const first = items.find(x => x.active);
const name = first?.name ?? "unknown";
JV
Java
items.stream()
    .filter(Item::isActive)
    .findFirst()
    .map(Item::getName)
    .orElse("unknown");

Common Mistakes

When coming from TypeScript, developers often make these mistakes:

  • Java streams are lazy — operations run only when a terminal op (collect/sum/findFirst) is called
  • stream() creates a stream; collect(toList()) materializes it back to a List
  • Optional<T> instead of undefined for findFirst/findAny
Common Pitfall
Don't assume Java works exactly like TypeScript. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Streams are lazy — nothing runs until a terminal operation (collect/sum/findFirst)
  • mapToInt/Double return primitive streams; use .sum()/.average() for numeric aggregates
  • Collectors: toList(), joining(","), groupingBy(key), counting(), toMap(k,v)
  • findFirst returns Optional<T>; chain with .map().orElse() to handle absent case
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your TypeScript code in Java to practice these concepts.
PreviousFinish