Что выведет код?
#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
Вопросы с собеседования 👩💻
Что такое Vector?
Что такое Vector?
Anonymous Quiz
21%
Массив фиксированного размера
13%
Коллекция уникальных элементов
64%
Устаревшая синхронизированная реализация списка
3%
Класс для работы с файлами
Collectors в Java
Collectors.collectingAndThen
Collectors.collectingAndThen – это особый коллектор, который добавляет финальное преобразование к результату другого коллектора. Это мощный инструмент для пост-обработки данных после сбора.
1. Базовая концепция
Сигнатура:
Где:
downstream – основной коллектор (например, toList, groupingBy)
finisher – функция, применяемая к результату коллектора
RR – новый тип возвращаемого значения
2. Простые примеры
2.1. Создание неизменяемого списка
2.2. Получение первого элемента
3. Комбинация с другими коллекторами
3.1. Группировка с неизменяемыми значениями
3.2. Подсчет с дополнительной проверкой
4. Внутренняя реализация
Принцип работы:
Сначала выполняется основной коллектор (downstream)
Затем к его результату применяется finisher
Возвращается преобразованное значение
Псевдокод:
5. Практические применения
5.1. Безопасное извлечение Optional
5.2. Нормализация данных после группировки
6. Особенности и ограничения
Порядок выполнения:
Сначала полностью выполняется downstream-коллектор
Затем применяется finisher
Параллельные стримы:
Работает корректно, если finisher – thread-safe
Null-обработка:
Finisher должен сам обрабатывать null-значения
7. Производительность
Обычно добавляет незначительные накладные расходы:
5-10% к времени выполнения базового коллектора
В HotSpot часто инлайнится JIT-компилятором
#Java #Training #Medium #Collectors #partitioningBy
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
Collectors в Java
Collectors.reducing
Collectors.reducing – это универсальный коллектор для выполнения операций свертки (reduction) над элементами потока. Он предоставляет более гибкую альтернативу встроенным операциям типа sum(), max() и min(), позволяя задавать собственную логику агрегации.
1. Три формы reducing
1.1. Базовая форма (с identity, mapper и операцией)
Параметры:
identity – начальное значение (аналог "нейтрального элемента")
mapper – функция преобразования элемента перед обработкой
op – операция для объединения двух значений
Пример (сумма длин строк):
1.2. Упрощенная форма (с identity и операцией)
Пример (конкатенация строк):
1.3. Минималистичная форма (только операция)
Пример (поиск максимального числа):
2. Внутренняя реализация
Принцип работы:
Для каждого элемента применяет mapper (если указан)
Накопление результата через op:
3. Практические примеры
3.1. Сложная агрегация объектов
3.2. Комбинация с groupingBy
3.3. Кастомная агрегация
4. Ограничения и нюансы
Параллельные стримы:
Требует ассоциативности операции ((a op b) op c == a op (b op c))
Пример ассоциативных операций: сложение, умножение, min/max
Неассоциативные: вычитание, деление
Null-значения:
identity не может быть null
Операция должна обрабатывать null, если mapper может его вернуть
Производительность:
Чуть медленнее специализированных коллекторов (summingInt и др.)
На 10-15% медленнее прямого reduce для примитивов
5. Альтернативы
Когда лучше использовать другие методы:
Для примитивов:
Для стандартных операций:
Для неизменяемых коллекций:
#Java #Training #Medium #Collectors #reducing
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
Что выведет код?
#Tasks
import java.util.*;
public class Task010525 {
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
Iterator<String> it = list.iterator();
list.add("D");
System.out.println(it.next());
}
}
#Tasks
Please open Telegram to view this post
VIEW IN TELEGRAM