{ }codelper

Learn › Java

Language-specific · 39

LINQ and streams

Chain filtering, mapping, grouping and sorting into one pipeline instead of writing loops.

This lesson is for Java · C#.
Python does the same with comprehensions (see the Python lesson), JavaScript and TypeScript chain the array methods filter, map and reduce, and C++20 has ranges such as std::views::filter.
C and Craykon use loops.

C#'s LINQ and Java's streams chain the steps "pick, change, group, sort, combine" as method calls in a pipeline.
The example below keeps only words longer than three letters, groups them by length and prints each group, then adds up all the remaining letters.

import java.util.*;import java.util.stream.*; public class Main {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        List<String> words = new ArrayList<>();        while (sc.hasNext()) words.add(sc.next());         List<String> longWords = words.stream()            .filter(w -> w.length() > 3)            .toList(); // 스트림은 한 번만 쓸 수 있어서 결과를 목록으로 담아 둬요         Map<Integer, List<String>> byLength = longWords.stream()            .collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.toList()));         byLength.entrySet().stream()            .map(e -> e.getKey() + ": " + e.getValue().stream().sorted().collect(Collectors.joining(" ")))            .forEach(System.out::println);         int total = longWords.stream().mapToInt(String::length).sum();        System.out.println("total letters: " + total);    }}
stdin
the quick brown fox jumps over the lazy dog quickly
stdout
4: lazy over
5: brown jumps quick
7: quickly
total letters: 30

Key points

  • Most work on lists is a mix of filtering, mapping, grouping, sorting and summing.
    Instead of a loop, you write those steps in order.
  • Each step has a matching name in both languages, such as filtering with Where/filter and mapping with Select/map.
  • A pipeline runs not when you build it but when you take results out.
  • If you will use the result more than once, store it in a list (ToList()/toList()).
  • Grouping gives keys, each with its group.
    If you need the keys in order, sort them yourself.

How each language writes it

Javaimport java.util.stream.*; — first take a stream from the list with words.stream(), then filter, map, sorted, Collectors.groupingBy, mapToInt(...).sum(), toList() (or collect(Collectors.toList())).
groupingBy uses a HashMap by default, which has no order, so the example asks for a TreeMap with groupingBy(String::length, TreeMap::new, Collectors.toList())
C#using System.Linq; — Where (filter), Select (map), OrderBy/ThenBy (Order() sorts by the values themselves), GroupBy, Sum, ToList().
GroupBy returns groups with a Key in first-seen order, so the example sorts them with OrderBy(g => g.Key).
The SQL-like query syntax from w in words where w.Length > 3 group w by w.Length compiles to the same method calls

Common mistakes

Using the same stream twiceJava

Stream<String> s = words.stream();
s.forEach(System.out::println);
long n = s.count();

→ IllegalStateException: stream has already been operated upon or closed — a stream allows only one terminal operation (forEach, collect, sum and so on)

Keep data you will reuse in a list and take a fresh stream() each time.

Handling numbers as Stream<Integer>Java

int sum = nums.stream().reduce(0, Integer::sum);

→ Each number is wrapped in an Integer object (boxing), which is slow, and there is no sum() method

mapToInt gives an IntStream with sum(), average() and max() ready to use (boxed() turns it back into objects).
C#'s IEnumerable<int> has no boxing.

Changing the source after building a LINQ queryC#

var q = nums.Where(n => n > 1);
nums.Add(5);
Console.WriteLine(q.Count());

→ q holds no result, only how to filter (deferred execution). It counts the new 5 as well, and every foreach or Count() runs it again from the start

To reuse a result you computed once, store it with ToList().

How languages differ
A pipeline runs when you take results out.
A C# query runs again every time and a Java stream can be used only once, so store results you will reuse with ToList()/toList().

Practice