← Назад к вопросам

Как можно лимитировать Stream?

1.3 Junior🔥 141 комментариев
#Stream API и функциональное программирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Как лимитировать Stream в Java

Основное решение: limit()

Метод limit(n) — это встроенный промежуточный оператор Stream API, который ограничивает количество элементов для обработки. Это самый простой и эффективный способ.

import java.util.stream.Stream;
import java.util.List;

// Пример 1: Базовое использование limit()
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

numbers.stream()
    .limit(5)  // Берём только первые 5 элементов
    .forEach(System.out::println);
// Вывод: 1, 2, 3, 4, 5

// Пример 2: Комбинация с фильтром
List<String> names = List.of("Alice", "Bob", "Charlie", "David", "Eve");

names.stream()
    .filter(name -> name.length() > 3)  // Фильтр: только длинные имена
    .limit(2)  // Но берём только первые 2
    .forEach(System.out::println);
// Вывод: Alice, Charlie

Практические примеры

Пример 1: Пагинация данных

public class UserService {
    private List<User> users = new ArrayList<>();
    
    // Получить N страниц по M элементов
    public List<User> getPage(int pageNumber, int pageSize) {
        return users.stream()
            .skip((long) pageNumber * pageSize)  // Пропустить предыдущие страницы
            .limit(pageSize)  // Взять размер страницы
            .collect(Collectors.toList());
    }
    
    // Пример использования
    List<User> page1 = getPage(0, 10);  // Элементы 0-9
    List<User> page2 = getPage(1, 10);  // Элементы 10-19
}

Пример 2: Получить TOP N элементов

List<Integer> scores = List.of(95, 87, 92, 88, 91, 76, 84, 89);

// Top 3 оценки (отсортированные по убыванию)
List<Integer> topScores = scores.stream()
    .sorted(Collections.reverseOrder())  // Сортируем от большего к меньшему
    .limit(3)  // Берём первые 3
    .collect(Collectors.toList());
// Результат: [95, 92, 91]

Пример 3: Обработка больших файлов

import java.nio.file.Files;
import java.nio.file.Paths;

public class FileProcessor {
    
    // Прочитать первые 100 строк файла (экономия памяти)
    public List<String> readFirstLines(String filename, int limit) throws IOException {
        return Files.lines(Paths.get(filename))
            .limit(limit)  // Берём только первые 100 строк
            .collect(Collectors.toList());
    }
}

Пример 4: Бесконечный Stream с лимитом

// Генерируем бесконечный поток
Stream.iterate(1, n -> n + 1)  // 1, 2, 3, 4, 5, ...
    .limit(10)  // БЕЗ limit() это зависнет!
    .forEach(System.out::println);
// Вывод: 1-10

// Другой пример: случайные числа
import java.util.Random;

Random random = new Random();
random.ints()  // Бесконечный поток целых чисел
    .limit(5)  // Ограничиваем 5 числами
    .forEach(System.out::println);

Комбинация limit() и skip()

List<Integer> data = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// Пропустить 3 элемента и взять 4 элемента (элементы с индексом 3-6)
List<Integer> middle = data.stream()
    .skip(3)    // Пропускаем первые 3
    .limit(4)   // Берём следующие 4
    .collect(Collectors.toList());
// Результат: [4, 5, 6, 7]

// Практическое применение: пагинация
public <T> List<T> paginate(List<T> items, int pageNumber, int pageSize) {
    return items.stream()
        .skip((long) pageNumber * pageSize)
        .limit(pageSize)
        .collect(Collectors.toList());
}

Optimized: ленивое вычисление

Важно понимать, что limit() работает лениво. Stream не обработает элементы дальше лимита:

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

numbers.stream()
    .peek(n -> System.out.println("Processing: " + n))  // Debug вывод
    .limit(3)
    .forEach(System.out::println);

// Вывод:
// Processing: 1
// Processing: 2
// Processing: 3
// 1
// 2
// 3
// ^^^ обработаны только 3 элемента, остальные 7 игнорируются!

Для параллельных Stream

List<Integer> largeData = List.of(/* миллионы элементов */);

// Параллельный stream с лимитом
largeData.parallelStream()
    .filter(n -> n % 2 == 0)  // Фильтр четных
    .limit(1000)  // Берём первые 1000 четных
    .collect(Collectors.toList());

// ⚠️ Внимание: limit() на параллельных streams менее эффективен!
// Может потребоваться больше обработки, чем на обычных

Альтернативные подходы (редко используются)

1. Фильтр с счётчиком (не рекомендуется)

// ❌ Плохо — сложнее читается, медленнее
AtomicInteger counter = new AtomicInteger(0);
List<String> result = names.stream()
    .filter(n -> counter.getAndIncrement() < 5)
    .collect(Collectors.toList());

2. Массив или подсписок (не для Stream)

// ❌ Плохо — не используем возможности Stream
List<Integer> limited = numbers.subList(0, Math.min(5, numbers.size()));

// ✅ Хорошо — используем Stream
List<Integer> limited = numbers.stream()
    .limit(5)
    .collect(Collectors.toList());

Работа с Optional и limit()

import java.util.Optional;

// limit() с optional
Optional<Integer> firstElement = numbers.stream()
    .limit(1)
    .findFirst();  // Гарантированно вернёт first элемент если есть

// Или проще:
Optional<Integer> firstElement = numbers.stream()
    .findFirst();  // Не нужен limit для первого элемента

Практический совет

Для больших коллекций (100k+) всегда используй limit() перед финальной операцией (collect, forEach), это сэкономит память и улучшит производительность. Особенно важно при работе с фильтрами и сортировкой — сначала фильтруй/сортируй, потом лимитируй.