Логотип Workflow

Article

Updated at:

Stream Api And Optional

Тема 10. Stream API and Optional

Stream API и Optional в Java нужны, чтобы делать код обработки данных более предсказуемым и читаемым. Stream помогает описывать преобразования как конвейер шагов, а Optional заставляет явно обработать сценарий «значения нет». Для новичка это два инструмента, которые резко уменьшают хаос с null и вложенными циклами.

Stream pipeline

1. Как мыслить Stream API

Stream — это не контейнер, а pipeline вычислений над источником данных. Источник может быть коллекцией, массивом, файлом.

Базовая модель:

  1. source (источник),
  2. промежуточные операции (filter, map, flatMap ...),
  3. терминальная операция (toList, collect, count, findFirst ...).

Пример:

List<String> names = List.of("ann", "bob", "alex", "anna");

List<String> result = names.stream()
        .filter(n -> n.length() > 3)
        .map(String::toUpperCase)
        .sorted()
        .toList();

2. Ленивая природа stream

Промежуточные операции ленивые. Пока нет терминальной операции, реального прохода по данным нет.

Stream<String> s = names.stream().filter(n -> n.startsWith("a"));
// вычисления еще не выполнены
long count = s.count(); // запуск pipeline

Это помогает оптимизировать цепочки и не делать лишнюю работу.

3. Частые операции с примерами

map — преобразовать каждый элемент

List<Integer> lengths = names.stream()
        .map(String::length)
        .toList();

filter — оставить элементы по условию

List<String> shortNames = names.stream()
        .filter(n -> n.length() <= 3)
        .toList();

flatMap — «распаковать» вложенные коллекции

List<List<String>> tags = List.of(
        List.of("java", "backend"),
        List.of("backend", "spring")
);

List<String> uniqueTags = tags.stream()
        .flatMap(List::stream)
        .distinct()
        .sorted()
        .toList();

collect и groupingBy

Map<Integer, List<String>> byLength = names.stream()
        .collect(java.util.stream.Collectors.groupingBy(String::length));

reduce для свертки

List<Integer> prices = List.of(100, 250, 80, 70);
int total = prices.stream().reduce(0, Integer::sum);

4. Когда stream лучше, а когда лучше цикл

СитуацияПодход
Линейная трансформацияStream
Сложные ветвления и состояниеЦикл
Агрегации и группировкиStream
Детальная пошаговая отладкаЦикл

Прагматичное правило: выбирайте не «модный» стиль, а тот, который легче читать и поддерживать команде.

5. Optional: явный контракт «значение может отсутствовать»

Optional<T> показывает, что значение необязательно присутствует.

Optional<String> email = findEmailByUserId(10L);
String value = email.orElse("[email protected]");

Это лучше неявного возврата null, потому что вызывающий код вынужден обработать оба сценария.

6. Базовые операции Optional

  • orElse, orElseGet, orElseThrow
  • map, flatMap, filter
  • ifPresent, ifPresentOrElse

Пример цепочки без null-веток:

Optional<User> user = findUser(id);
String city = user
        .map(User::getAddress)
        .map(Address::getCity)
        .filter(c -> !c.isBlank())
        .orElse("Unknown");

7. Важный нюанс orElse vs orElseGet

String a = opt.orElse(expensiveFallback());
String b = opt.orElseGet(() -> expensiveFallback());

orElse вычисляет fallback всегда, даже если значение уже есть. orElseGet — ленивый вариант.

8. Частые ошибки новичков

  1. Длинные stream-цепочки с тяжелой бизнес-логикой внутри лямбд.
  2. Побочные эффекты внутри map/filter.
  3. Использование Optional.get() без гарантии присутствия.
  4. Применение parallelStream() без замеров.

9. Примеры «как лучше»

Плохо:

String city = user.getAddress().getCity(); // NullPointerException risk

Лучше:

String city = Optional.ofNullable(user)
        .map(User::getAddress)
        .map(Address::getCity)
        .orElse("Unknown");

Плохо:

list.stream().map(x -> { log(x); return transform(x); }).toList();

Лучше разделять вычисление и побочные эффекты.

10. Что важно запомнить

Stream API — про декларативную обработку данных. Optional — про честный контракт отсутствия значения. В связке они помогают писать чище, если соблюдать меру: короткие понятные цепочки, минимум побочных эффектов и осознанный выбор между stream и обычным циклом.

Авторизуйтесь чтоб пройти тесты

Quiz

Проверьте, что вы усвоили

Practice

Интерактивная практика

Выполните задания и сразу проверьте ответ.