Stream collect и Collectors

collect — главная терминальная операция Stream API, когда нужно собрать результат в коллекцию или агрегат. Она принимает Collector — стратегию сборки. В классе java.util.stream.Collectors уже есть готовые сборщики на все типовые случаи: список, множество, словарь, строку, группировку, подсчёт и так далее.

Часто используемые Collectors

Collectors.toList()
Collectors.toSet()
Collectors.toMap(keyFn, valueFn)
Collectors.joining(", ", "[", "]")
Collectors.counting()
Collectors.summingInt(fn) / averagingInt(fn)
Collectors.groupingBy(classifier)
Collectors.partitioningBy(predicate)

Примеры

Пример 1. toList и toSet

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

public class CollectBasic {
    public static void main(String[] args) {
        List<Integer> raw = Arrays.asList(1, 2, 2, 3, 3, 3);
        List<Integer> list = raw.stream().collect(Collectors.toList());
        Set<Integer> set = raw.stream().collect(Collectors.toSet());
        System.out.println(list);
        System.out.println(set);
    }
}

Output:

[1, 2, 2, 3, 3, 3]
[1, 2, 3]

Пример 2. joining

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class JoiningDemo {
    public static void main(String[] args) {
        List<String> sensors = Arrays.asList("ИК", "УЗ", "Линия");
        String csv = sensors.stream().collect(Collectors.joining(", "));
        String pretty = sensors.stream().collect(Collectors.joining(", ", "[", "]"));
        System.out.println(csv);
        System.out.println(pretty);
    }
}

Output:

ИК, УЗ, Линия
[ИК, УЗ, Линия]

Пример 3. toMap

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ToMapDemo {
    public static void main(String[] args) {
        List<String> sensors = Arrays.asList("ик", "уз", "линия");
        Map<String, Integer> lens = sensors.stream()
                .collect(Collectors.toMap(s -> s, String::length));
        System.out.println(lens);
    }
}

Output:

{ик=2, уз=2, линия=5}

Пример 4. groupingBy

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GroupingDemo {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("ик", "уз", "линия", "led", "серво", "вкл");
        Map<Integer, List<String>> byLen = words.stream()
                .collect(Collectors.groupingBy(String::length));
        System.out.println(byLen);
    }
}

Output:

{2=[ик, уз], 3=[led, вкл], 5=[линия, серво]}

Пример 5. groupingBy + counting

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GroupingCount {
    public static void main(String[] args) {
        List<String> events = Arrays.asList("старт", "стоп", "старт", "пауза", "стоп", "старт");
        Map<String, Long> counts = events.stream()
                .collect(Collectors.groupingBy(e -> e, Collectors.counting()));
        System.out.println(counts);
    }
}

Output:

{пауза=1, стоп=2, старт=3}

Пример 6. partitioningBy

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class PartitionDemo {
    public static void main(String[] args) {
        List<Integer> distances = Arrays.asList(5, 12, 30, 8, 100, 3);
        Map<Boolean, List<Integer>> parts = distances.stream()
                .collect(Collectors.partitioningBy(d -> d >= 10));
        System.out.println("Близко: " + parts.get(false));
        System.out.println("Далеко: " + parts.get(true));
    }
}

Output:

Близко: [5, 8, 3]
Далеко: [12, 30, 100]

Подводные камни

Предупреждение

  • Collectors.toMap бросит IllegalStateException, если встретит дубликат ключа. Используйте перегрузку с merge-функцией: toMap(k, v, (a, b) -> a).

  • Порядок ключей в Collectors.toMap не гарантируется. Нужен порядок — используйте toMap(..., LinkedHashMap::new).

  • В Java 16+ есть Stream.toList() — возвращает неизменяемый список, в отличие от Collectors.toList().

  • groupingBy создаёт пустой список для отсутствующих групп только если вы сами их добавите — он группирует только то, что реально пришло.

  • joining принимает CharSequence: элементы потока должны быть строками (или предварительно превращены через map).

См. также

Примечание

Материал подготовлен по мотивам документации пакета java.util.stream и класса java.util.stream.Collectors, распространяется в рамках Oracle Free Documentation License. Текст написан своими словами, примеры кода — оригинальные.