Collectors в Java
Collectors.toMap
Collectors.toMap – это мощный коллектор, преобразующий элементы потока в Map<K, V>, где ключи и значения вычисляются на основе элементов потока.
1. Базовая форма: toMap(keyMapper, valueMapper)
Преобразует поток в Map, где:
keyMapper – функция, вычисляющая ключ,
valueMapper – функция, вычисляющая значение.
Сигнатура:
Пример:
2. Обработка дубликатов: toMap(keyMapper, valueMapper, mergeFunction)
Если возможны повторяющиеся ключи, нужно указать mergeFunction – стратегию разрешения конфликтов.
Сигнатура:
Примеры:
2.1. Оставить старое значение
2.2. Объединить значения (например, конкатенация строк)
2.3. Суммирование значений при дублировании ключей
3. Выбор реализации Map: toMap(keyMapper, valueMapper, mergeFunction, mapFactory)
Позволяет указать конкретную реализацию Map (например, LinkedHashMap или TreeMap).
Сигнатура:
Примеры:
3.1. Сохранение порядка вставки (LinkedHashMap)
3.2. Сортировка по ключу (TreeMap)
#Java #Training #Medium #Collectors #CollectorsToMap
Collectors.toMap
Collectors.toMap – это мощный коллектор, преобразующий элементы потока в Map<K, V>, где ключи и значения вычисляются на основе элементов потока.
1. Базовая форма: toMap(keyMapper, valueMapper)
Преобразует поток в Map, где:
keyMapper – функция, вычисляющая ключ,
valueMapper – функция, вычисляющая значение.
Сигнатура:
public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper
)
Пример:
List<String> names = List.of("Alice", "Bob", "Charlie");
// Создаем Map: {имя -> длина строки}
Map<String, Integer> nameToLength = names.stream()
.collect(Collectors.toMap(
name -> name, // ключ – сама строка
name -> name.length() // значение – длина строки
));
// Результат: {"Alice":5, "Bob":3, "Charlie":7}
Что произойдет, если ключи повторяются?
→ Будет выброшено IllegalStateException!
2. Обработка дубликатов: toMap(keyMapper, valueMapper, mergeFunction)
Если возможны повторяющиеся ключи, нужно указать mergeFunction – стратегию разрешения конфликтов.
Сигнатура:
public static <T, K, U> Collector<T, ?, Map<K, U>> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction
)
Примеры:
2.1. Оставить старое значение
List<String> names = List.of("Alice", "Bob", "Alice");
Map<String, Integer> nameToLength = names.stream()
.collect(Collectors.toMap(
name -> name,
name -> name.length(),
(oldValue, newValue) -> oldValue // при конфликте берем старое значение
));
// Результат: {"Alice":5, "Bob":3}
2.2. Объединить значения (например, конкатенация строк)
List<String> names = List.of("A", "B", "A", "C");
Map<String, String> mergedLetters = names.stream()
.collect(Collectors.toMap(
letter -> letter,
letter -> letter,
(oldVal, newVal) -> oldVal + newVal // "A" + "A" → "AA"
));
// Результат: {"A":"AA", "B":"B", "C":"C"}
2.3. Суммирование значений при дублировании ключей
record Product(String id, double price) {}
List<Product> products = List.of(
new Product("A", 10.0),
new Product("B", 20.0),
new Product("A", 15.0)
);
Map<String, Double> totalPriceById = products.stream()
.collect(Collectors.toMap(
Product::id,
Product::price,
Double::sum // 10.0 + 15.0 = 25.0
));
// Результат: {"A":25.0, "B":20.0}
3. Выбор реализации Map: toMap(keyMapper, valueMapper, mergeFunction, mapFactory)
Позволяет указать конкретную реализацию Map (например, LinkedHashMap или TreeMap).
Сигнатура:
public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(
Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapFactory
)
Примеры:
3.1. Сохранение порядка вставки (LinkedHashMap)
Map<String, Integer> orderedMap = names.stream()
.collect(Collectors.toMap(
name -> name,
name -> name.length(),
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
));
3.2. Сортировка по ключу (TreeMap)
Map<String, Integer> sortedMap = names.stream()
.collect(Collectors.toMap(
name -> name,
name -> name.length(),
(oldVal, newVal) -> oldVal,
TreeMap::new
));
#Java #Training #Medium #Collectors #CollectorsToMap
4. Особенности и подводные камни
4.1. Null-ключи и Null-значения
HashMap допускает один null-ключ, но если keyMapper возвращает null – будет NullPointerException.
Если valueMapper возвращает null, значение в Map будет null.
4.2. Параллельные стримы
toMap не оптимизирован для параллельных операций.
Вместо него лучше использовать Collectors.toConcurrentMap.
4.3. Изменяемые ключи
Если ключ изменяется после добавления в Map, это может нарушить работу HashMap (ключи должны быть immutable).
5. Альтернативы
5.1. Collectors.toUnmodifiableMap (Java 10+)
Создает неизменяемую Map:
5.2. Collectors.toConcurrentMap
Оптимизирован для многопоточности:
#Java #Training #Medium #Collectors #CollectorsToMap
4.1. Null-ключи и Null-значения
HashMap допускает один null-ключ, но если keyMapper возвращает null – будет NullPointerException.
Если valueMapper возвращает null, значение в Map будет null.
4.2. Параллельные стримы
toMap не оптимизирован для параллельных операций.
Вместо него лучше использовать Collectors.toConcurrentMap.
4.3. Изменяемые ключи
Если ключ изменяется после добавления в Map, это может нарушить работу HashMap (ключи должны быть immutable).
5. Альтернативы
5.1. Collectors.toUnmodifiableMap (Java 10+)
Создает неизменяемую Map:
Map<String, Integer> unmodifiableMap = names.stream()
.collect(Collectors.toUnmodifiableMap(
name -> name,
name -> name.length()
));
5.2. Collectors.toConcurrentMap
Оптимизирован для многопоточности:
ConcurrentMap<String, Integer> concurrentMap = names.stream()
.collect(Collectors.toConcurrentMap(
name -> name,
name -> name.length()
));
#Java #Training #Medium #Collectors #CollectorsToMap
Что выведет код?
#Tasks
import java.util.*;
public class Task280425 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
List<Integer> subList = list.subList(1, 3);
subList.add(4);
System.out.println(list.size());
}
}
#Tasks
Please open Telegram to view this post
VIEW IN TELEGRAM
Вопросы с собеседования 👩💻
Что такое Checked Exception?
Что такое Checked Exception?
Anonymous Quiz
24%
Исключение, которое не нужно обрабатывать
70%
Исключение, которое компилятор требует обработать
0%
Ошибка компиляции
5%
Класс для работы с потоками
Collectors в Java
groupingBy
Collectors.groupingBy – один из самых мощных коллекторов в Java Stream API, позволяющий группировать элементы потока по заданному критерию. Возвращает Map<K, List<T>>, где ключ (K) – это значение классификатора, а значение – список элементов, соответствующих этому ключу.
1. Базовая форма: groupingBy(classifier)
Группирует элементы по ключу, возвращаемому classifier (функцией классификации).
Сигнатура:
Пример:
Как это работает внутри?
Классификатор (String::length) вычисляет ключ для каждого элемента.
Элементы с одинаковым ключом добавляются в List<T>.
По умолчанию используется HashMap, а значения хранятся в ArrayList.
2. groupingBy(classifier, downstream) – с дополнительным коллектором
Позволяет указать, как собирать элементы в группы (не только в List).
Сигнатура:
Примеры:
2.1. Группировка в Set вместо List
2.2. Подсчет количества элементов в каждой группе
2.3. Объединение элементов в строку
3. groupingBy(classifier, mapFactory, downstream) – с выбором реализации Map
Позволяет указать конкретную реализацию Map (например, TreeMap или LinkedHashMap).
Сигнатура:
Пример:
4. Многокритериальная группировка (вложенные groupingBy)
Можно группировать по нескольким уровням, создавая сложные структуры данных.
Пример:
5. Производительность и особенности
Порядок элементов в группах зависит от исходного потока (если он ordered, как в List, порядок сохраняется).
Хэш-коллизии обрабатываются через HashMap.
Нюансы при параллельных стримах:
Если используется ConcurrentHashMap, группировка становится потокобезопасной.
В некоторых случаях groupingByConcurrent работает быстрее.
#Java #Training #Medium #Collectors #groupingBy
groupingBy
Collectors.groupingBy – один из самых мощных коллекторов в Java Stream API, позволяющий группировать элементы потока по заданному критерию. Возвращает Map<K, List<T>>, где ключ (K) – это значение классификатора, а значение – список элементов, соответствующих этому ключу.
1. Базовая форма: groupingBy(classifier)
Группирует элементы по ключу, возвращаемому classifier (функцией классификации).
Сигнатура:
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(
Function<? super T, ? extends K> classifier
)
Пример:
List<String> names = List.of("Alice", "Bob", "Charlie", "Anna", "Alex");
Map<Integer, List<String>> groupedByLength = names.stream()
.collect(Collectors.groupingBy(String::length));
// Результат:
// {3=[Bob], 4=[Anna, Alex], 5=[Alice], 7=[Charlie]}
Как это работает внутри?
Классификатор (String::length) вычисляет ключ для каждого элемента.
Элементы с одинаковым ключом добавляются в List<T>.
По умолчанию используется HashMap, а значения хранятся в ArrayList.
2. groupingBy(classifier, downstream) – с дополнительным коллектором
Позволяет указать, как собирать элементы в группы (не только в List).
Сигнатура:
public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(
Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream
)
Примеры:
2.1. Группировка в Set вместо List
Map<Integer, Set<String>> groupedByLength = names.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.toSet()
));
2.2. Подсчет количества элементов в каждой группе
Map<Integer, Long> countByLength = names.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.counting()
));
// Результат: {3=1, 4=2, 5=1, 7=1}
2.3. Объединение элементов в строку
Map<Integer, String> joinedNames = names.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.joining(", ")
));
// Результат: {3="Bob", 4="Anna, Alex", 5="Alice", 7="Charlie"}
3. groupingBy(classifier, mapFactory, downstream) – с выбором реализации Map
Позволяет указать конкретную реализацию Map (например, TreeMap или LinkedHashMap).
Сигнатура:
public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(
Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream
)
Пример:
// Группировка с сохранением порядка (LinkedHashMap)
LinkedHashMap<Integer, List<String>> orderedGroups = names.stream()
.collect(Collectors.groupingBy(
String::length,
LinkedHashMap::new,
Collectors.toList()
));
// Группировка с сортировкой по ключу (TreeMap)
TreeMap<Integer, Set<String>> sortedGroups = names.stream()
.collect(Collectors.groupingBy(
String::length,
TreeMap::new,
Collectors.toSet()
));
4. Многокритериальная группировка (вложенные groupingBy)
Можно группировать по нескольким уровням, создавая сложные структуры данных.
Пример:
record Person(String name, String city, int age) {}
List<Person> people = List.of(
new Person("Alice", "NY", 25),
new Person("Bob", "LA", 30),
new Person("Charlie", "NY", 25),
new Person("David", "LA", 35)
);
// Группировка по городу, а затем по возрасту
Map<String, Map<Integer, List<Person>>> multiLevelGroup = people.stream()
.collect(Collectors.groupingBy(
Person::city,
Collectors.groupingBy(Person::age)
));
// Результат:
// {
// "NY": {25=[Alice, Charlie]},
// "LA": {30=[Bob], 35=[David]}
// }
5. Производительность и особенности
Порядок элементов в группах зависит от исходного потока (если он ordered, как в List, порядок сохраняется).
Хэш-коллизии обрабатываются через HashMap.
Нюансы при параллельных стримах:
Если используется ConcurrentHashMap, группировка становится потокобезопасной.
В некоторых случаях groupingByConcurrent работает быстрее.
#Java #Training #Medium #Collectors #groupingBy
Пишем тестовое задание от реального работодателя для новичков! Часть 3 (финал).
Встреча от 27.04.25
Запись встречи -
YOUTUBE
RUTUBE
На сегодняшней встрече мы с @Shikin_Anatoliy дописали тестовое задание от реального работодателя.
Что мы успели:
➡️ Реализовали создание матчей.
➡️ Продумали сложную логику генерации данных для турнирной таблицы
➡️ Конечно же допустили ошибки, и столкнулись с результатами неправильного проектирования.
Хотелось бы извиниться за склейки в финале видео, из-за неизвестного неадеквата😞
Смотрите, комментируйте, задавайте вопросы! Обязательно подписывайтесь на ютуб и рутюб каналы!!!
Надеюсь видео будет полезным для новичков!
Встреча от 27.04.25
Запись встречи -
YOUTUBE
RUTUBE
На сегодняшней встрече мы с @Shikin_Anatoliy дописали тестовое задание от реального работодателя.
Что мы успели:
Хотелось бы извиниться за склейки в финале видео, из-за неизвестного неадеквата
Смотрите, комментируйте, задавайте вопросы! Обязательно подписывайтесь на ютуб и рутюб каналы!!!
Надеюсь видео будет полезным для новичков!
Please open Telegram to view this post
VIEW IN TELEGRAM
Collectors в Java
Collectors.summingInt
Collectors.summingInt — это специализированный коллектор, предназначенный для суммирования целочисленных значений, извлекаемых из элементов потока. Он особенно полезен, когда нужно вычислить общую сумму числовых характеристик объектов.
1. Базовая форма и назначение
Сигнатура:
Где:
mapper — функция, преобразующая элемент потока в int (значение, которое нужно суммировать)
Возвращает коллектор, который применяет эту функцию ко всем элементам и суммирует результаты
Пример:
2. Внутренняя реализация
Коллектор работает по следующему алгоритму:
Использует промежуточный аккумулятор типа int[] (для мутабельного накопления суммы)
Для каждого элемента применяет mapper и добавляет результат в аккумулятор
В конце возвращает итоговую сумму
3. Особенности и ограничения
3.1. Только для примитивных int
Для long используйте summingLong
Для double — summingDouble
3.2. Нет обработки null
Если mapper возвращает null, будет NullPointerException
3.3. Параллельные стримы
Корректно работает в параллельных стримах благодаря атомарному накоплению
4. Альтернативы
4.1. Через mapToInt() + sum()
Более прямолинейный способ:
Плюсы:
Чуть лучше производительность
Более явное преобразование типов
Минусы:
Нельзя использовать в комбинации с другими коллекторами (например, при группировке)
4.2. reduce()
5. Комбинирование с другими коллекторами
Особенно полезно в groupingBy для агрегации:
Пример вывода:
6. Производительность
Для примитивов (summingInt) работает быстрее, чем reduce с Integer
В HotSpot JIT-компилятор может оптимизировать до прямого сложения
#Java #Training #Medium #Collectors #summingInt
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
Что выведет код?
#Tasks
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
Это после многодневной поддержки легаси кода, такой приход случается? 🧐 😃
https://t.me/Java_for_beginner_dev
#Mems
https://t.me/Java_for_beginner_dev
#Mems
Please open Telegram to view this post
VIEW IN TELEGRAM
Вопросы с собеседования 👩💻
Что такое WeakHashMap?
Что такое WeakHashMap?
Anonymous Quiz
26%
Коллекция с сильными ссылками
57%
Коллекция, где ключи могут быть удалены сборщиком мусора
4%
Отсортированный список
13%
Класс для работы с датами
Collectors в Java
Collectors.joining
Collectors.joining – это специализированный коллектор для конкатенации строковых представлений элементов потока в единую строку. Это мощный инструмент для работы с текстовыми данными в Java Stream API.
1. Базовая форма: joining()
Конкатенирует элементы без разделителей.
Сигнатура:
Пример:
Особенности:
Работает только с CharSequence (String, StringBuilder и др.)
Если поток пуст, возвращает пустую строку ""
2. С разделителем: joining(delimiter)
Добавляет указанный разделитель между элементами.
Сигнатура:
Пример:
Применение:
CSV-форматирование
Построение SQL-запросов
Логирование коллекций
3. Полная форма: joining(delimiter, prefix, suffix)
Добавляет префикс и суффикс к результату.
Сигнатура:
Пример:
Типичные сценарии:
JSON-массивы: joining(", ", "[", "]")
SQL IN-условия: joining("', '", "'", "'") → 'Java', 'Stream', 'API'
Форматированные списки
4. Внутренняя реализация
Коллектор использует StringJoiner внутри:
Особенности работы:
Для пустого потока возвращает prefix + suffix (например, "[]")
Оптимизирован для String (избегает лишних преобразований)
В параллельных стримах работает корректно (слияние через StringJoiner.merge)
5. Работа с не-строковыми объектами
Если элементы не являются строками, нужно преобразовать их явно:
Альтернатива через Collectors.mapping:
6. Ограничения и нюансы
Null-элементы:
Вызывают NullPointerException
Решение – фильтрация:
Большие объемы данных:
Для гигантских потоков лучше использовать StringBuilder напрямую
Локализация:
Нет встроенной поддержки (для чисел/дат используйте NumberFormat перед joining)
7. Комбинирование с другими коллекторами
Группировка с объединением:
Результат:
Многоуровневое объединение:
8. Альтернативы
StringBuilder (для сложных сценариев):
String.join() (только для List<String>):
TextBlocks (Java 15+ для многострочных данных):
#Java #Training #Medium #Collectors #joining
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
Collectors в Java
Collectors.partitioningBy
Collectors.partitioningBy – это специальный коллектор, который разделяет элементы потока на две группы с помощью предиката.
Результатом всегда является Map<Boolean, List<T>>, где:
true – элементы, удовлетворяющие условию
false – элементы, не удовлетворяющие условию
1. Базовая форма: partitioningBy(predicate)
Разделяет элементы на две группы без дополнительной обработки.
Сигнатура:
Пример:
Особенности:
Всегда возвращает обе группы (даже если одна пустая)
Сохраняет порядок элементов, если поток упорядочен
2. Расширенная форма: partitioningBy(predicate, downstream)
Позволяет дополнительно обработать каждую группу с помощью другого коллектора.
Сигнатура:
Примеры применения:
2.1. Подсчет количества элементов в каждой группе
2.2. Объединение элементов в строку
2.3. Группировка в Set вместо List
3. Внутренняя реализация
Коллектор работает по следующему алгоритму:
Создает Map<Boolean, List<T>> с двумя ключами (true/false)
Для каждого элемента применяет предикат и добавляет в соответствующую группу
Если указан downstream-коллектор, применяет его к каждой группе
Псевдокод реализации:
4. Практические примеры
4.1. Разделение пользователей по возрасту
4.2. Статистика по разделенным группам
5. Ограничения и нюансы
Неизменяемость результатов:
Возвращаемые коллекции можно модифицировать (обычно ArrayList)
Для неизменяемых групп использовать collectingAndThen:
Параллельные стримы:
Работает корректно в параллельных стримах
Группы объединяются через Map.merge
Null-значения:
Предикат должен обрабатывать null (иначе NPE)
Решение:
#Java #Training #Medium #Collectors #partitioningBy
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
Что выведет код?
#Tasks
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