Приведи пример промежуточных операций в Stream API
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Промежуточные операции в Stream API
Stream API в Java предоставляет функциональный подход к обработке коллекций. Промежуточные операции (Intermediate Operations) — это операции, которые возвращают новый Stream и позволяют строить цепочки преобразований. Они выполняются ленивым образом (lazy) — только при вызове терминальной операции.
1. filter() — фильтрация элементов
Отбирает элементы, соответствующие условию:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Оставляем только четные числа
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// Результат: [2, 4, 6, 8, 10]
// Более сложная фильтрация
List<String> words = Arrays.asList("java", "stream", "api", "java");
List<String> filtered = words.stream()
.filter(w -> w.length() > 3)
.filter(w -> !w.equals("java")) // Можно цеплять несколько filter
.collect(Collectors.toList());
// Результат: ["stream"]
2. map() — преобразование элементов
Преобразует каждый элемент в новый элемент:
List<String> words = Arrays.asList("hello", "world");
// Преобразуем строки в их длину
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
// Результат: [5, 5]
// Преобразование объектов
List<User> users = Arrays.asList(
new User(1, "Alice"),
new User(2, "Bob")
);
List<String> userNames = users.stream()
.map(User::getName)
.collect(Collectors.toList());
// Результат: ["Alice", "Bob"]
// Преобразование в объект другого типа
List<UserDto> dtos = users.stream()
.map(user -> new UserDto(user.getId(), user.getName().toUpperCase()))
.collect(Collectors.toList());
3. flatMap() — развертывание (flatten) потоков
Преобразует каждый элемент в Stream и объединяет все потоки в один:
List<String> sentences = Arrays.asList(
"hello world",
"java stream",
"api test"
);
// Развертываем предложения в отдельные слова
List<String> words = sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
.collect(Collectors.toList());
// Результат: ["hello", "world", "java", "stream", "api", "test"]
// Более сложный пример с коллекциями
List<List<Integer>> numbers = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
List<Integer> flattened = numbers.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
// Результат: [1, 2, 3, 4, 5, 6]
// Фильтрация и развертывание
List<User> users = Arrays.asList(
new User(1, "Alice", Arrays.asList("java", "python")),
new User(2, "Bob", Arrays.asList("java", "go"))
);
List<String> skills = users.stream()
.flatMap(user -> user.getSkills().stream())
.distinct()
.collect(Collectors.toList());
// Результат: ["java", "python", "go"]
4. distinct() — удаление дубликатов
Если элементы наследуют Object, используется equals/hashCode:
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
List<Integer> unique = numbers.stream()
.distinct()
.collect(Collectors.toList());
// Результат: [1, 2, 3, 4]
// С объектами (нужны корректные equals/hashCode)
List<User> users = Arrays.asList(
new User(1, "Alice"),
new User(1, "Alice"), // Дубликат
new User(2, "Bob")
);
List<User> uniqueUsers = users.stream()
.distinct()
.collect(Collectors.toList());
// Результат: [User(1, Alice), User(2, Bob)]
5. sorted() — сортировка
Сортирует элементы в натуральном порядке или по компаратору:
List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3);
// Сортировка в натуральном порядке
List<Integer> sorted = numbers.stream()
.sorted()
.collect(Collectors.toList());
// Результат: [1, 2, 3, 5, 8, 9]
// Сортировка в обратном порядке
List<Integer> descending = numbers.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
// Результат: [9, 8, 5, 3, 2, 1]
// Сортировка объектов
List<User> users = Arrays.asList(
new User(3, "Charlie"),
new User(1, "Alice"),
new User(2, "Bob")
);
List<User> sortedByName = users.stream()
.sorted(Comparator.comparing(User::getName))
.collect(Collectors.toList());
// Результат: [Alice, Bob, Charlie]
// Сортировка по нескольким полям
List<User> sorted = users.stream()
.sorted(Comparator.comparing(User::getAge)
.thenComparing(User::getName))
.collect(Collectors.toList());
6. peek() — инспекция без изменения
Выполняет действие для каждого элемента без изменения потока (полезно для отладки):
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = numbers.stream()
.filter(n -> n % 2 == 0)
.peek(n -> System.out.println("Найдено четное число: " + n))
.map(n -> n * n)
.peek(n -> System.out.println("Результат квадрата: " + n))
.collect(Collectors.toList());
// Печать:
// Найдено четное число: 2
// Результат квадрата: 4
// Найдено четное число: 4
// Результат квадрата: 16
// Результат: [4, 16]
// Практический пример логирования
List<User> processed = users.stream()
.peek(user -> logger.info("Processing user: {}", user.getName()))
.filter(user -> user.isActive())
.peek(user -> logger.debug("User {} is active", user.getName()))
.collect(Collectors.toList());
7. limit() и skip() — ограничение и пропуск
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Первые 5 элементов
List<Integer> first5 = numbers.stream()
.limit(5)
.collect(Collectors.toList());
// Результат: [1, 2, 3, 4, 5]
// Пропустить первые 3 элемента
List<Integer> skipped = numbers.stream()
.skip(3)
.collect(Collectors.toList());
// Результат: [4, 5, 6, 7, 8, 9, 10]
// Пагинация: элементы 6-10
List<Integer> page2 = numbers.stream()
.skip(5) // Пропустить первую страницу (размер 5)
.limit(5) // Взять 5 элементов
.collect(Collectors.toList());
// Результат: [6, 7, 8, 9, 10]
Цепочка промежуточных операций
List<User> users = Arrays.asList(
new User(1, "Alice", 25),
new User(2, "Bob", 30),
new User(3, "Charlie", 22),
new User(4, "Diana", 28)
);
// Сложная цепочка преобразований
List<String> result = users.stream()
.filter(user -> user.getAge() >= 25) // Фильтруем по возрасту
.sorted(Comparator.comparing(User::getName)) // Сортируем по имени
.map(User::getName) // Преобразуем в имена
.map(String::toUpperCase) // В заглавные буквы
.limit(2) // Берем первых двух
.peek(name -> System.out.println("Name: " + name)) // Логируем
.collect(Collectors.toList());
// Печать:
// Name: ALICE
// Name: DIANA
// Результат: ["ALICE", "DIANA"]
Важные особенности
- Ленивость (Laziness): промежуточные операции не выполняются до вызова терминальной операции
- Цепочка методов: позволяет писать выразительный, декларативный код
- Неизменяемость: исходная коллекция не изменяется
- Производительность: Stream API оптимизирует вычисления, пропуская ненужные операции
Промежуточные операции — основа функционального программирования в Java, позволяя писать чистый и понятный код для обработки коллекций.