JavaRush /Курсы /JAVA 25 SELF /Методы sum, count, average, max, min в Stream API

Методы sum, count, average, max, min в Stream API

JAVA 25 SELF
31 уровень , 0 лекция
Открыта

1. Подсчёт элементов: count()

Когда вы работаете с коллекциями чисел или объектов, почти всегда возникает задача посчитать что-то: количество элементов, сумму, среднее, максимум или минимум. Например, количество сотрудников в компании, сумму всех зарплат, средний возраст студентов, самый дорогой продукт, максимальный заказ. С появлением Stream API всё стало проще и выразительнее.

Как это работает

Метод count() — терминальный оператор Stream API, который возвращает количество элементов в потоке.

long count = employees.stream().count();

Если нужно посчитать только те элементы, которые соответствуют условию — используйте фильтр filter(...):

long richCount = employees.stream()
    .filter(e -> e.getSalary() > 100_000)
    .count();

Пример для нашего приложения:

Пусть есть список продуктов:

List<Product> products = List.of(
    new Product("Молоко", 80),
    new Product("Сыр", 250),
    new Product("Хлеб", 40)
);

Посчитаем, сколько продуктов дороже 100:

long expensiveCount = products.stream()
    .filter(p -> p.getPrice() > 100)
    .count();

System.out.println("Дорогих продуктов: " + expensiveCount);

Что такое Optional

В Java есть специальный контейнер — Optional. Это объект-обёртка, который может содержать значение или быть пустым. Он используется в случаях, когда результата может не быть, и это нужно явно показать. Например, методы min(), max() и average() могут вернуть пустой результат, если коллекция пуста. Вместо null возвращается Optional.

Основные методы Optional

  • isPresent() / isEmpty() — проверка, есть ли значение.
  • orElse(значение) — вернуть значение или дефолт, если пусто.
  • orElseThrow() — выбросить исключение, если пусто.
  • ifPresent(...) — выполнить действие только при наличии значения.

Пример:

OptionalInt min = products.stream()
    .mapToInt(Product::getPrice)
    .min();

if (min.isPresent()) {
    System.out.println("Минимальная цена: " + min.getAsInt());
} else {
    System.out.println("Список пуст");
}

Или короче:

int minValue = min.orElse(-1);
System.out.println("Минимальная цена: " + minValue);

Такой подход позволяет избегать NullPointerException и делает код более безопасным.

2. Сумма, среднее, минимум и максимум для числовых данных

Примитивные стримы: IntStream, DoubleStream, LongStream

Для числовых данных Stream API предлагает специальные стримы: IntStream, DoubleStream, LongStream. Они позволяют легко и быстро вычислять сумму, среднее, минимум и максимум.

Преобразование в числовой стрим

Чтобы получить числовой стрим из потока объектов, используйте методы mapToInt, mapToDouble, mapToLong.

int sum = products.stream()
    .mapToInt(Product::getPrice)
    .sum();
System.out.println("Общая сумма цен: " + sum);

Методы

  • sum() — сумма всех элементов.
  • average() — среднее арифметическое (возвращает OptionalDouble).
  • min(), max() — минимум и максимум (возвращают OptionalInt/OptionalDouble/OptionalLong).

Примеры:

// Сумма всех цен
int total = products.stream()
    .mapToInt(Product::getPrice)
    .sum();

// Средняя цена
OptionalDouble avg = products.stream()
    .mapToInt(Product::getPrice)
    .average();

// Минимальная и максимальная цена
OptionalInt min = products.stream()
    .mapToInt(Product::getPrice)
    .min();

OptionalInt max = products.stream()
    .mapToInt(Product::getPrice)
    .max();

Как получить значение из Optional

double average = avg.orElse(0.0); // 0.0, если коллекция пуста
int minValue = min.orElse(-1);    // -1, если коллекция пуста
int maxValue = max.orElse(-1);    // -1, если коллекция пуста

Шутка программиста: пустой список — это не ошибка, это просто ситуация, когда у вас нет данных. Но если вы попытаетесь взять значение из пустого Optional методом getAsInt() — поймаете NoSuchElementException. Всегда используйте orElse или isPresent()!

3. Агрегация объектов: Collectors.summingInt, averagingInt, maxBy, minBy

Если у вас поток объектов, и вы хотите агрегировать значения по какому-то признаку, используйте специальные коллекторы из класса Collectors.

summingInt, averagingInt

int totalSalary = employees.stream()
    .collect(Collectors.summingInt(Employee::getSalary));

double avgSalary = employees.stream()
    .collect(Collectors.averagingInt(Employee::getSalary));

minBy, maxBy

Чтобы найти объект с максимальным/минимальным значением поля:

Optional<Employee> richest = employees.stream()
    .collect(Collectors.maxBy(Comparator.comparingInt(Employee::getSalary)));

Optional<Employee> poorest = employees.stream()
    .collect(Collectors.minBy(Comparator.comparingInt(Employee::getSalary)));

Важно: результат — Optional, потому что коллекция может быть пуста.

Пример

Создадим класс Product:

public class Product {
    private final String name;
    private final int price;
    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }
    public String getName() { return name; }
    public int getPrice() { return price; }
}

Создадим список продуктов:

List<Product> products = List.of(
    new Product("Молоко", 80),
    new Product("Сыр", 250),
    new Product("Хлеб", 40)
);

Найдём самый дорогой продукт:

Optional<Product> maxProduct = products.stream()
    .collect(Collectors.maxBy(Comparator.comparingInt(Product::getPrice)));

maxProduct.ifPresent(p -> System.out.println("Самый дорогой продукт: " + p.getName()));

4. Комбинирование с группировкой

Агрегирующие методы часто сочетаются с группировкой. Например, можно найти среднюю цену продуктов по первой букве названия:

Map<Character, Double> avgPriceByLetter = products.stream()
    .collect(Collectors.groupingBy(
        p -> p.getName().charAt(0),
        Collectors.averagingInt(Product::getPrice)
    ));

System.out.println(avgPriceByLetter);

Результат:

{М=80.0, С=250.0, Х=40.0}

5. Примеры из реальной жизни

Пример 1: Студенты и оценки

Список студентов и их баллы:

public class Student {
    private final String name;
    private final int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    public String getName() { 
        return name; 
    }
    public int getScore() { 
        return score; 
    }
}

List<Student> students = List.of(
    new Student("Алиса", 90),
    new Student("Боб", 75),
    new Student("Вася", 100)
);

Сколько студентов с баллом выше 80?

long count = students.stream()
    .filter(s -> s.getScore() > 80)
    .count();
System.out.println("Студентов с баллом > 80: " + count);

Средний балл:

double avg = students.stream()
    .mapToInt(Student::getScore)
    .average()
    .orElse(0.0);
System.out.println("Средний балл: " + avg);

Самый успешный студент:

students.stream()
    .max(Comparator.comparingInt(Student::getScore))
    .ifPresent(s -> System.out.println("Лучший студент: " + s.getName()));

Пример 2: Количество уникальных товаров

long uniqueCount = products.stream()
    .map(Product::getName)
    .distinct()
    .count();
System.out.println("Уникальных товаров: " + uniqueCount);

6. Полезные нюансы

Визуальная схема: как работают агрегирующие методы

Коллекция объектов
   │
   ▼
stream()
   │
   ▼
mapToInt/Double/Long (если нужно)
   │
   ▼
sum() / average() / min() / max() / count()
   │
   ▼
Результат (int, double, long, Optional)

Обработка Optional: как безопасно извлекать значения

Почему методы возвращают Optional?
Если коллекция пуста (например, вы ищете максимум среди пустого списка), возвращается пустой Optional. Если сразу вызвать getAsInt()/get(), будет выброшено исключение.

Как правильно:

  • Использовать orElse(значение по умолчанию)
  • Использовать ifPresent(...) для действий только при наличии значения
  • Использовать orElseThrow(...) для явного выбрасывания исключения

Пример:

OptionalDouble avg = products.stream()
    .mapToInt(Product::getPrice)
    .average();

System.out.println("Средняя цена: " + avg.orElse(0.0));

Когда использовать примитивные стримы, а когда — Collectors

  • Если нужно просто посчитать сумму/среднее/минимум/максимум по числовому полю — используйте mapToInt и соответствующий метод (sum, average, min, max).
  • Если хотите агрегировать объекты (например, найти объект с максимальным полем), используйте коллекторы maxBy/minBy.
  • Если нужно сгруппировать и посчитать агрегаты по группам — используйте комбинацию groupingBy с агрегирующими коллекторами.

7. Типичные ошибки при работе с агрегирующими методами

Ошибка №1: Не обработан Optional. Часто новички забывают, что min, max, average возвращают Optional. В результате при попытке получить значение из пустого контейнера возникает NoSuchElementException. Всегда используйте orElse, orElseThrow или ifPresent.

Ошибка №2: Использование обычного stream вместо mapToInt/mapToDouble. Если вы пишете stream().sum(), компилятор скажет: «Такого метода нет!». Для суммы, среднего, минимума и максимума нужны примитивные стримы.

Ошибка №3: Несовпадение типов при сравнении. Когда ищете максимум/минимум среди объектов, используйте правильный компаратор: Comparator.comparingInt(Employee::getSalary). Иначе можно получить ошибку компиляции или некорректный результат.

Ошибка №4: Сравнение объектов с null. Если в коллекции могут быть null-элементы, агрегирование может выбрасывать NullPointerException. Лучше предварительно фильтровать такие элементы или использовать Comparator.nullsLast(...)/nullsFirst(...).

Ошибка №5: Использование sum/average/min/max с пустыми коллекциями без учета результата. Если коллекция пуста, метод sum() вернёт 0, а average()/min()/max() — пустой Optional. Не забывайте обрабатывать этот случай.

1
Задача
JAVA 25 SELF, 31 уровень, 0 лекция
Недоступна
Анализ цен в вашем онлайн-магазине 🛍️
Анализ цен в вашем онлайн-магазине 🛍️
1
Задача
JAVA 25 SELF, 31 уровень, 0 лекция
Недоступна
Отчёт об успеваемости студентов в Академии Магии 🎓
Отчёт об успеваемости студентов в Академии Магии 🎓
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
21 декабря 2025
Вначале размазываем на 5 уровней элементарные вещи, и в конце каждого уровня по 4 задачи, чтоб уж точно не забыть. Сейчас - в одной лекции рассказали про 20 новых методов, агрегацию, группировку, 2 задачки и гуляй 😁
Andrey Уровень 1
27 сентября 2025
31