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

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Пишем тестовое задание от реального работодателя для новичков! Часть 3 (финал).

Встреча от 27.04.25

Запись встречи -
YOUTUBE
RUTUBE

На сегодняшней встрече мы с @Shikin_Anatoliy дописали тестовое задание от реального работодателя.

Что мы успели:
➡️ Реализовали создание матчей.
➡️ Продумали сложную логику генерации данных для турнирной таблицы
➡️ Конечно же допустили ошибки, и столкнулись с результатами неправильного проектирования.

Хотелось бы извиниться за склейки в финале видео, из-за неизвестного неадеквата
😞

Смотрите, комментируйте, задавайте вопросы! Обязательно подписывайтесь на ютуб и рутюб каналы!!!

Надеюсь видео будет полезным для новичков!
Please open Telegram to view this post
VIEW IN TELEGRAM
Collectors в Java

Collectors.summingInt

Collectors.summingInt — это специализированный коллектор, предназначенный для суммирования целочисленных значений, извлекаемых из элементов потока. Он особенно полезен, когда нужно вычислить общую сумму числовых характеристик объектов.

1. Базовая форма и назначение

Сигнатура:
public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper)


Где:

mapper — функция, преобразующая элемент потока в int (значение, которое нужно суммировать)
Возвращает коллектор, который применяет эту функцию ко всем элементам и суммирует результаты


Пример:

List<Product> products = List.of(
new Product("Laptop", 1000),
new Product("Phone", 500),
new Product("Tablet", 300)
);

int totalPrice = products.stream()
.collect(Collectors.summingInt(Product::getPrice));

// Результат: 1800 (1000 + 500 + 300)


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

Коллектор работает по следующему алгоритму:
Использует промежуточный аккумулятор типа int[] (для мутабельного накопления суммы)
Для каждого элемента применяет mapper и добавляет результат в аккумулятор
В конце возвращает итоговую сумму


3. Особенности и ограничения

3.1. Только для примитивных int

Для long используйте summingLong
Для double — summingDouble


3.2. Нет обработки null

Если mapper возвращает null, будет NullPointerException

3.3. Параллельные стримы

Корректно работает в параллельных стримах благодаря атомарному накоплению

4. Альтернативы

4.1. Через mapToInt() + sum()

Более прямолинейный способ:
int total = products.stream()
.mapToInt(Product::getPrice)
.sum();


Плюсы:

Чуть лучше производительность
Более явное преобразование типов


Минусы:
Нельзя использовать в комбинации с другими коллекторами (например, при группировке)

4.2. reduce()

int total = products.stream()
.reduce(0, (sum, p) -> sum + p.getPrice(), Integer::sum);


5. Комбинирование с другими коллекторами


Особенно полезно в groupingBy для агрегации:

Map<String, Integer> totalPriceByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.summingInt(Product::getPrice)
));


Пример вывода:

{
"Electronics": 1800,
"Office": 750
}


6. Производительность


Для примитивов (summingInt) работает быстрее, чем reduce с Integer
В HotSpot JIT-компилятор может оптимизировать до прямого сложения


#Java #Training #Medium #Collectors #summingInt
Что выведет код?

import java.util.*;

public class Task290425 {
public static void main(String[] args) {
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3));
Set<Integer> set2 = new HashSet<>(Arrays.asList(3, 2, 1));
Set<Integer> set3 = new LinkedHashSet<>(Arrays.asList(1, 2, 3));

System.out.println(set1.equals(set2));
System.out.println(set1.equals(set3));
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
17%
true, false
20%
false, true
43%
true, true
20%
false, false
Это после многодневной поддержки легаси кода, такой приход случается? 🧐 😃

https://t.me/Java_for_beginner_dev

#Mems
Please open Telegram to view this post
VIEW IN TELEGRAM
Collectors в Java

Collectors.joining

Collectors.joining – это специализированный коллектор для конкатенации строковых представлений элементов потока в единую строку. Это мощный инструмент для работы с текстовыми данными в Java Stream API.

1. Базовая форма: joining()

Конкатенирует элементы без разделителей.

Сигнатура:
public static Collector<CharSequence, ?, String> joining()


Пример:
List<String> words = List.of("Java", "Stream", "API");
String result = words.stream().collect(Collectors.joining());

// Результат: "JavaStreamAPI"


Особенности:
Работает только с CharSequence (String, StringBuilder и др.)
Если поток пуст, возвращает пустую строку ""


2. С разделителем: joining(delimiter)

Добавляет указанный разделитель между элементами.

Сигнатура:
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)


Пример:
String result = words.stream().collect(Collectors.joining(", "));

// Результат: "Java, Stream, API"


Применение:
CSV-форматирование
Построение SQL-запросов
Логирование коллекций


3. Полная форма: joining(delimiter, prefix, suffix)

Добавляет префикс и суффикс к результату.

Сигнатура:
public static Collector<CharSequence, ?, String> joining(
CharSequence delimiter,
CharSequence prefix,
CharSequence suffix
)


Пример:
String result = words.stream()
.collect(Collectors.joining(", ", "[", "]"));

// Результат: "[Java, Stream, API]"


Типичные сценарии:
JSON-массивы: joining(", ", "[", "]")
SQL IN-условия: joining("', '", "'", "'") → 'Java', 'Stream', 'API'
Форматированные списки


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

Коллектор использует StringJoiner внутри:
StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix);
stream.forEach(element -> joiner.add(element.toString()));
return joiner.toString();


Особенности работы:
Для пустого потока возвращает prefix + suffix (например, "[]")
Оптимизирован для String (избегает лишних преобразований)
В параллельных стримах работает корректно (слияние через StringJoiner.merge)


5. Работа с не-строковыми объектами

Если элементы не являются строками, нужно преобразовать их явно:
List<Integer> numbers = List.of(1, 2, 3);
String result = numbers.stream()
.map(Object::toString)
.collect(Collectors.joining("-"));

// Результат: "1-2-3"


Альтернатива через Collectors.mapping:
String result = numbers.stream()
.collect(Collectors.mapping(
Object::toString,
Collectors.joining("/")
));


6. Ограничения и нюансы

Null-элементы:

Вызывают NullPointerException

Решение – фильтрация:

.filter(Objects::nonNull)


Большие объемы данных:
Для гигантских потоков лучше использовать StringBuilder напрямую

Локализация:
Нет встроенной поддержки (для чисел/дат используйте NumberFormat перед joining)

7. Комбинирование с другими коллекторами

Группировка с объединением:

Map<String, String> joinedByCategory = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.mapping(
Product::getName,
Collectors.joining(", ")
)
));


Результат:
{
"Electronics": "Laptop, Phone",
"Office": "Pen, Paper"
}


Многоуровневое объединение:
String complex = persons.stream()
.collect(Collectors.mapping(
p -> p.name() + " (" + p.age() + ")",
Collectors.joining("; ", "Persons: ", ".")
));

// Результат: "Persons: Alice (25); Bob (30)."


8. Альтернативы

StringBuilder (для сложных сценариев):
StringBuilder sb = new StringBuilder();
list.forEach(sb::append);


String.join() (только для List<String>):
String.join(", ", words);


TextBlocks (Java 15+ для многострочных данных):
String sql = """
SELECT %s
FROM table
WHERE id IN (%s)
""".formatted(
columns.stream().collect(joining(", ")),
ids.stream().map(Object::toString).collect(joining(", "))
);


#Java #Training #Medium #Collectors #joining
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Collectors в Java

Collectors.partitioningBy

Collectors.partitioningBy – это специальный коллектор, который разделяет элементы потока на две группы с помощью предиката.

Результатом всегда является Map<Boolean, List<T>>, где:
true – элементы, удовлетворяющие условию
false – элементы, не удовлетворяющие условию


1. Базовая форма: partitioningBy(predicate)
Разделяет элементы на две группы без дополнительной обработки.

Сигнатура:
public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(
Predicate<? super T> predicate
)


Пример:
List<Integer> numbers = List.of(1, 2, 3, 4, 5);

Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));

// Результат:
// {
// false=[1, 3, 5],
// true=[2, 4]
// }


Особенности:
Всегда возвращает обе группы (даже если одна пустая)
Сохраняет порядок элементов, если поток упорядочен


2. Расширенная форма: partitioningBy(predicate, downstream)
Позволяет дополнительно обработать каждую группу с помощью другого коллектора.

Сигнатура:
public static <T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(
Predicate<? super T> predicate,
Collector<? super T, A, D> downstream
)


Примеры применения:

2.1. Подсчет количества элементов в каждой группе
Map<Boolean, Long> countByPartition = numbers.stream()
.collect(Collectors.partitioningBy(
n -> n % 2 == 0,
Collectors.counting()
));

// Результат: {false=3, true=2}


2.2. Объединение элементов в строку
Map<Boolean, String> joinedPartitions = numbers.stream()
.collect(Collectors.partitioningBy(
n -> n % 2 == 0,
Collectors.mapping(
Object::toString,
Collectors.joining("-")
)
));

// Результат: {false="1-3-5", true="2-4"}


2.3. Группировка в Set вместо List
Map<Boolean, Set<Integer>> toSet = numbers.stream()
.collect(Collectors.partitioningBy(
n -> n % 2 == 0,
Collectors.toSet()
));


3. Внутренняя реализация
Коллектор работает по следующему алгоритму:
Создает Map<Boolean, List<T>> с двумя ключами (true/false)
Для каждого элемента применяет предикат и добавляет в соответствующую группу
Если указан downstream-коллектор, применяет его к каждой группе


Псевдокод реализации:
Map<Boolean, A> result = new HashMap<>();
result.put(true, new ArrayList<>());
result.put(false, new ArrayList<>());

stream.forEach(item -> {
boolean key = predicate.test(item);
result.get(key).add(item);
});

// Если есть downstream:
result.replaceAll((k, v) -> downstream.finisher().apply(v));



4. Практические примеры
4.1. Разделение пользователей по возрасту
record Person(String name, int age) {}

List<Person> people = List.of(
new Person("Alice", 25),
new Person("Bob", 17),
new Person("Charlie", 30)
);

Map<Boolean, List<Person>> adults = people.stream()
.collect(Collectors.partitioningBy(p -> p.age() >= 18));

// Результат:
// {
// false=[Person[name=Bob, age=17]],
// true=[Person[name=Alice, age=25], Person[name=Charlie, age=30]]
// }


4.2. Статистика по разделенным группам
Map<Boolean, IntSummaryStatistics> stats = numbers.stream()
.collect(Collectors.partitioningBy(
n -> n % 2 == 0,
Collectors.summarizingInt(Integer::intValue)
));

// Получить среднее для четных чисел:
double avgEven = stats.get(true).getAverage();


5. Ограничения и нюансы
Неизменяемость результатов:
Возвращаемые коллекции можно модифицировать (обычно ArrayList)

Для неизменяемых групп использовать collectingAndThen:
.collect(Collectors.collectingAndThen(
Collectors.partitioningBy(predicate),
Collections::unmodifiableMap
));


Параллельные стримы:
Работает корректно в параллельных стримах
Группы объединяются через Map.merge


Null-значения:
Предикат должен обрабатывать null (иначе NPE)

Решение:
.partitioningBy(item -> item != null && predicate.test(item))


#Java #Training #Medium #Collectors #partitioningBy
Что выведет код?

import java.util.*;

public class Task300425 {
public static void main(String[] args) {
short a = 1;
int i = 1;
long k = 1;

Map<Object, String> map = new HashMap<>();
map.put(i, "Java");
map.put(k, "Python");
map.put(a, "C++");

System.out.println(map.size());
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
14%
1
7%
2
48%
3
31%
Exception какой-то
#Mems. Небольшое несоответствие получилось.
Тут много нюансов...😂😂😂

https://t.me/Java_for_beginner_dev

#Mems
Collectors в Java

Collectors.collectingAndThen

Collectors.collectingAndThen – это особый коллектор, который добавляет финальное преобразование к результату другого коллектора. Это мощный инструмент для пост-обработки данных после сбора.

1. Базовая концепция

Сигнатура:
public static <T, A, R, RR> Collector<T, A, RR> collectingAndThen(
Collector<T, A, R> downstream,
Function<R, RR> finisher
)


Где:
downstream – основной коллектор (например, toList, groupingBy)
finisher – функция, применяемая к результату коллектора
RR – новый тип возвращаемого значения

2. Простые примеры

2.1. Создание неизменяемого списка
List<String> immutableList = stream
.collect(Collectors.collectingAndThen(
Collectors.toList(),
Collections::unmodifiableList
));


2.2. Получение первого элемента
String firstItem = stream
.collect(Collectors.collectingAndThen(
Collectors.toList(),
list -> list.isEmpty() ? null : list.get(0)
);


3. Комбинация с другими коллекторами


3.1. Группировка с неизменяемыми значениями
Map<String, List<Integer>> unmodifiableGroups = numbers.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(
n -> n % 2 == 0 ? "even" : "odd"
),
Collections::unmodifiableMap
));


3.2. Подсчет с дополнительной проверкой

Long countOrZero = stream
.collect(Collectors.collectingAndThen(
Collectors.counting(),
c -> c > 100 ? c : 0L
));


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

Принцип работы:
Сначала выполняется основной коллектор (downstream)
Затем к его результату применяется finisher
Возвращается преобразованное значение


Псевдокод:
R intermediateResult = downstream.collect(stream);
RR finalResult = finisher.apply(intermediateResult);
return finalResult;


5. Практические применения


5.1. Безопасное извлечение Optional
Optional<String> lastElement = stream
.collect(Collectors.collectingAndThen(
Collectors.toList(),
list -> list.isEmpty()
? Optional.empty()
: Optional.of(list.get(list.size()-1))
);


5.2. Нормализация данных после группировки

Map<String, Set<String>> caseInsensitiveGroups = words.stream()
.collect(Collectors.collectingAndThen(
Collectors.groupingBy(
String::toLowerCase,
Collectors.toSet()
),
Collections::unmodifiableMap
));


6. Особенности и ограничения


Порядок выполнения:
Сначала полностью выполняется downstream-коллектор
Затем применяется finisher


Параллельные стримы:
Работает корректно, если finisher – thread-safe

Null-обработка:
Finisher должен сам обрабатывать null-значения

7. Производительность

Обычно добавляет незначительные накладные расходы:
5-10% к времени выполнения базового коллектора
В HotSpot часто инлайнится JIT-компилятором


#Java #Training #Medium #Collectors #partitioningBy
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN 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