C#
JV

C# to Java

10 lessons

Progress0%
1Introduction2Type Systems3Properties & Getters4Generics5Collections6LINQ to Streams7Async Programming8Ecosystem9Modern Java Features10Concurrency
All Mirror Courses
C#
JV
LINQ to Streams
MirrorLesson 6 of 10
Lesson 6

LINQ to Streams

LINQ to Streams

Introduction

In this lesson, you'll learn about linq to streams in Java. Coming from C#, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
C#
From C#:

In C#, you're familiar with linq to streams.

JV
In Java:

Java has its own approach to linq to streams, 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
List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * 2)
    .sorted()
    .collect(Collectors.toList());

Comparing to C#

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

C#
C# (What you know)
var evens = numbers
    .Where(n => n % 2 == 0)
    .Select(n => n * 2)
    .OrderBy(n => n)
    .ToList();
Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

LINQ Where/Select map to Stream filter/map

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

C# ToList() becomes .collect(Collectors.toList()) — more verbose

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

Lambda syntax: n => body (C#) vs n -> body (Java)

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

Both are lazy — no work happens until a terminal operation

Mirror Card
C#
From C#:

You may be used to different syntax or behavior.

JV
In Java:

Java streams are single-use; iterating twice throws IllegalStateException

Step-by-Step Breakdown

1. LINQ Operator Mapping

Every LINQ operator has a Stream equivalent. The names differ but the concepts are identical.

C#
C#
list.Where(x => x > 0)
list.Select(x => x * 2)
list.OrderBy(x => x)
list.First()
list.Count()
list.Any(x => x > 0)
list.Sum()
JV
Java
list.stream().filter(x -> x > 0)
list.stream().map(x -> x * 2)
list.stream().sorted()
list.stream().findFirst()
list.stream().count()
list.stream().anyMatch(x -> x > 0)
list.stream().mapToInt(x -> x).sum()

2. Collecting Results

C#'s ToList()/ToArray() are terminal operations. Java needs .collect() with a Collector.

C#
C#
var list = query.ToList();
var arr  = query.ToArray();
var dict = query.ToDictionary(x => x.Id);
JV
Java
List<T> list = stream.collect(Collectors.toList());
T[] arr = stream.toArray(T[]::new);
Map<K,V> dict = stream.collect(
    Collectors.toMap(x -> x.getId(), x -> x));
Rule of Thumb
Java 16+ adds stream.toList() as a shortcut — use it instead of the verbose collect form.

3. GroupBy and Grouping

LINQ GroupBy returns IGrouping elements. Java Collectors.groupingBy returns a Map.

C#
C#
var byDept = employees
    .GroupBy(e => e.Department)
    .ToDictionary(g => g.Key, g => g.ToList());
JV
Java
Map<String, List<Employee>> byDept = employees.stream()
    .collect(Collectors.groupingBy(e -> e.getDepartment()));

4. Single-Use Streams

Unlike LINQ (which re-queries on each iteration), a Java Stream can only be consumed once. Store results or call stream() again.

C#
C#
// C# — safe to iterate query multiple times
var query = list.Where(x => x > 0);
int count = query.Count();
var first = query.First(); // works fine
JV
Java
// Java — stream is consumed after first terminal op
Stream<Integer> s = list.stream().filter(x -> x > 0);
long count = s.count();
// s.findFirst() — THROWS IllegalStateException!

// Solution: collect first, or create fresh streams
List<Integer> filtered = list.stream().filter(x -> x > 0).toList();
long count2 = filtered.size();
Optional<Integer> first = filtered.stream().findFirst();
Common Pitfall
Never store a Stream in a field or reuse it. Create a new stream from the source each time you need to query.

Common Mistakes

When coming from C#, developers often make these mistakes:

  • LINQ Where/Select map to Stream filter/map
  • C# ToList() becomes .collect(Collectors.toList()) — more verbose
  • Lambda syntax: n => body (C#) vs n -> body (Java)
Common Pitfall
Don't assume Java works exactly like C#. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Where/Select/OrderBy → filter/map/sorted
  • Terminate with .collect(Collectors.toList()) or .toList() (Java 16+)
  • Both are lazy pipelines; work executes at the terminal operation
  • Java streams are single-use — collect results before reusing
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your C# code in Java to practice these concepts.
PreviousNext