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

В каком интерфейсе содержится Stream

1.7 Middle🔥 231 комментариев
#Stream API и функциональное программирование

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

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

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

В каком интерфейсе содержится Stream

Stream API — один из самых важных дополнений в Java 8. Stream находится в интерфейсе java.util.stream.Stream<T>, но это не случайно расположенный класс — это часть функционально-ориентированного API Java.

Где находится Stream

import java.util.stream.Stream;  // Базовый интерфейс Stream
import java.util.stream.IntStream;   // Для примитивов int
import java.util.stream.LongStream;  // Для примитивов long
import java.util.stream.DoubleStream;  // Для примитивов double

Stream — это интерфейс, а не класс. Его реализацию создают различные источники данных:

  • java.util.Collection (через метод stream())
  • java.util.stream.IntStream, LongStream, DoubleStream (для примитивов)
  • Массивы (через Arrays.stream())
  • Файлы (через Files.lines())
  • Другие источники

Как создать Stream

1. Из Collection (через Iterable)

Основной способ — через метод stream() в интерфейсе Iterable<T>:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Создание Stream из List
Stream<String> stream = names.stream();

// Обработка
stream.filter(name -> name.startsWith("A"))
      .forEach(System.out::println);
// Вывод: Alice

При этом Collection наследует от Iterable, и в Java 8+ добавлен default метод stream():

// В интерфейсе Iterable<E>
public interface Iterable<T> {
    Iterator<T> iterator();
    
    // Default метод, добавленный в Java 8
    default Stream<T> stream() {
        return StreamSupport.stream(
            Spliterator.spliterator(this, Spliterator.ORDERED),
            false
        );
    }
}

2. Из массивов

int[] numbers = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(numbers);

String[] words = {"Java", "Stream", "API"};
Stream<String> stringStream = Arrays.stream(words);

Double[] decimals = {1.5, 2.7, 3.2};
Stream<Double> doubleStream = Arrays.stream(decimals);

3. Создание Stream явно

// Пустой Stream
Stream<String> empty = Stream.empty();

// Stream из элементов
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);

// Stream из одного элемента
Stream<String> single = Stream.of("Hello");

// Бесконечный Stream (с limit)
Stream<Long> infinite = Stream.iterate(0L, n -> n + 1).limit(10);

// Генерирующий Stream
Stream<Double> generated = Stream.generate(Math::random).limit(5);

4. Из файлов

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

try (Stream<String> lines = Files.lines(Paths.get("file.txt"))) {
    lines.filter(line -> !line.isEmpty())
         .forEach(System.out::println);
}

5. Из Pattern.splitAsStream()

import java.util.regex.Pattern;

String text = "Java,Stream,API";
Pattern.compile(",")
       .splitAsStream(text)
       .forEach(System.out::println);

Иерархия Stream интерфейсов

// Главный интерфейс для reference types
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    // Промежуточные операции (lazy)
    Stream<T> filter(Predicate<? super T> predicate);
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
    Stream<T> distinct();
    Stream<T> sorted();
    
    // Терминальные операции (eager)
    void forEach(Consumer<? super T> action);
    long count();
    Optional<T> findFirst();
    Optional<T> findAny();
    boolean anyMatch(Predicate<? super T> predicate);
    Object[] toArray();
    // ... и другие
}

// Специализированные для примитивов
public interface IntStream extends BaseStream<Integer, IntStream> {
    IntStream filter(IntPredicate predicate);
    IntStream map(IntUnaryOperator mapper);
    // ...
}

Полный пример работы со Stream

import java.util.List;
import java.util.stream.Collectors;

public class StreamExample {
    
    public static void main(String[] args) {
        List<Person> people = List.of(
            new Person("Alice", 30),
            new Person("Bob", 25),
            new Person("Charlie", 35),
            new Person("Diana", 28)
        );
        
        // Промежуточные операции (ленивые)
        // Каждая операция возвращает новый Stream
        var result = people.stream()
            .filter(p -> p.getAge() >= 30)  // Filter
            .map(Person::getName)            // Map
            .sorted()                         // Sort
            .collect(Collectors.toList());    // Терминальная операция
        
        System.out.println(result);  // [Alice, Charlie]
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() { return name; }
    public int getAge() { return age; }
}

Промежуточные vs Терминальные операции

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// ПРОМЕЖУТОЧНЫЕ (ленивые) - возвращают Stream
Stream<String> filtered = names.stream()  // Stream<String>
    .filter(n -> n.length() > 3);          // Stream<String>
    // .map(String::toUpperCase)            // Stream<String>
    // Операция ещё не выполнена!

// ТЕРМИНАЛЬНЫЕ (жадные) - выполняют всю цепочку
long count = filtered.count();  // Теперь выполняется!

// Пример с явной ленивостью
var stream = names.stream()
    .peek(n -> System.out.println("Seen: " + n))  // Не выполнится!
    .filter(n -> n.length() > 3);

// Терминальная операция - только теперь выполнится
stream.forEach(System.out::println);  // Видим "Seen: ..." и результат

Параллельные Stream

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

// Последовательный Stream (default)
Stream<Integer> sequential = numbers.stream();

// Параллельный Stream
Stream<Integer> parallel = numbers.parallelStream();

// Пример использования
int sum = numbers.parallelStream()
    .filter(n -> n % 2 == 0)      // Четные числа
    .mapToInt(Integer::intValue)   // IntStream
    .sum();

System.out.println(sum);  // 2 + 4 + 6 + 8 + 10 = 30

Специализированные Streams для примитивов

// IntStream
IntStream intStream = IntStream.range(1, 5);  // 1, 2, 3, 4
intStream.forEach(System.out::println);

// LongStream
LongStream longStream = LongStream.rangeClosed(1, 5);  // 1 to 5 inclusive
longStream.sum();

// DoubleStream
DoubleStream doubleStream = DoubleStream.of(1.1, 2.2, 3.3);
doubleStream.average();

// Boxing/Unboxing
Stream<Integer> boxed = IntStream.range(1, 5).boxed();
IntStream unboxed = Stream.of(1, 2, 3).mapToInt(Integer::intValue);

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

// 1. Фильтрация и трансформация
List<String> result = names.stream()
    .filter(n -> n.startsWith("A"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

// 2. Группирование
Map<Integer, List<Person>> byAge = people.stream()
    .collect(Collectors.groupingBy(Person::getAge));

// 3. Партиция (разделение на две группы)
Map<Boolean, List<Person>> adults = people.stream()
    .collect(Collectors.partitioningBy(p -> p.getAge() >= 18));

// 4. Объединение Stream
Stream<String> combined = Stream.concat(
    names.stream(),
    Stream.of("Extra")
);

// 5. FlatMap для "развёртывания"
List<List<Integer>> lists = List.of(
    List.of(1, 2),
    List.of(3, 4),
    List.of(5, 6)
);
Stream<Integer> flat = lists.stream().flatMap(List::stream);

Ключевые моменты

  1. Stream находится в java.util.stream пакете — не в java.util
  2. Stream не хранит данные — это lazy pipeline обработки
  3. Stream можно использовать только один раз — после терминальной операции поток закончен
  4. По умолчанию Sequential — используй parallelStream() для параллельной обработки
  5. Ленивые операции — не выполняются до терминальной операции

Заключение

Stream API — это не просто отдельный интерфейс, а целая парадигма функционального программирования, встроенная в Java. Понимание иерархии Stream интерфейсов, способов их создания и операций критично для написания современного Java кода.

В каком интерфейсе содержится Stream | PrepBro