Java for Beginner
675 subscribers
558 photos
156 videos
12 files
852 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Collectors в Java

Collectors.
reducing

Collectors.reducing – это универсальный коллектор для выполнения операций свертки (reduction) над элементами потока. Он предоставляет более гибкую альтернативу встроенным операциям типа sum(), max() и min(), позволяя задавать собственную логику агрегации.

1. Три формы reducing
1.1. Базовая форма (с identity, mapper и операцией)
public static <T, U> Collector<T, ?, U> reducing(
U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op
)


Параметры:
identity – начальное значение (аналог "нейтрального элемента")
mapper – функция преобразования элемента перед обработкой
op – операция для объединения двух значений


Пример (сумма длин строк):
List<String> words = List.of("Java", "Stream", "API");
int totalLength = words.stream()
.collect(Collectors.reducing(
0, // identity
String::length, // mapper
Integer::sum // op
));
// Результат: 11 (4 + 6 + 1)


1.2. Упрощенная форма (с identity и операцией)
public static <T> Collector<T, ?, T> reducing(
T identity,
BinaryOperator<T> op
)


Пример (конкатенация строк):
String concatenated = words.stream()
.collect(Collectors.reducing(
"", // identity
String::concat
));
// Результат: "JavaStreamAPI"


1.3. Минималистичная форма (только операция)

public static <T> Collector<T, ?, Optional<T>> reducing(
BinaryOperator<T> op
)


Пример (поиск максимального числа):
Optional<Integer> max = Stream.of(3, 1, 4)
.collect(Collectors.reducing(
Integer::max
));
// Результат: Optional[4]


2. Внутренняя реализация

Принцип работы:
Для каждого элемента применяет mapper (если указан)

Накопление результата через op:
// Псевдокод реализации
U result = identity;
for (T element : stream) {
result = op.apply(result, mapper.apply(element));
}
return result;
Для формы без identity использует Optional для обработки пустых потоков


3. Практические примеры
3.1. Сложная агрегация объектов
record Product(String name, double price) {}

List<Product> products = List.of(
new Product("Laptop", 999.99),
new Product("Phone", 599.99)
);

// Сумма цен продуктов с названием длиннее 4 символов
double total = products.stream()
.collect(Collectors.reducing(
0.0,
p -> p.name().length() > 4 ? p.price() : 0,
Double::sum
));


3.2. Комбинация с groupingBy
// Максимальная цена в каждой категории
Map<String, Optional<Product>> byCategory = products.stream()
.collect(Collectors.groupingBy(
Product::category,
Collectors.reducing((p1, p2) ->
p1.price() > p2.price() ? p1 : p2
));


3.3. Кастомная агрегация
// Пользовательская свертка для статистики
class Stats {
int count;
double sum;
// + конструкторы/методы
}

Stats stats = stream.collect(Collectors.reducing(
new Stats(0, 0.0),
num -> new Stats(1, num),
(s1, s2) -> new Stats(
s1.count + s2.count,
s1.sum + s2.sum
)
));


4. Ограничения и нюансы
Параллельные стримы:
Требует ассоциативности операции ((a op b) op c == a op (b op c))
Пример ассоциативных операций: сложение, умножение, min/max
Неассоциативные: вычитание, деление


Null-значения:
identity не может быть null
Операция должна обрабатывать null, если mapper может его вернуть


Производительность:
Чуть медленнее специализированных коллекторов (summingInt и др.)
На 10-15% медленнее прямого reduce для примитивов


5. Альтернативы
Когда лучше использовать другие методы:

Для примитивов:
// Вместо:
.collect(reducing(0, Integer::sum))

// Лучше:
.mapToInt().sum()


Для стандартных операций:
// Вместо:
.collect(reducing(Integer::max))

// Лучше:
.max(Comparator.naturalOrder())


Для неизменяемых коллекций:
// Вместо:
.collect(reducing(Collections.emptyList(), customMerge))

// Лучше:
.collect(toUnmodifiableList())


#Java #Training #Medium #Collectors #reducing