Методы Stream API

Author: Tatyana Milkina

Методы Stream API делятся на две категории::

  • Оконечные (terminal)
  • Промежуточные (intermediate)
    • Без сохранения состояния (stateless)
    • С сохранением состояния (stateful)

1. Terminal методы

Оконечные операции завершают работу со Stream, потребляя его. После выполнения терминальной операции повторное использование потока невозможно.

Примеры терминальных методов:

  • forEach()
  • collect()
  • count()
  • min(), max()
  • findFirst(), findAny()
  • allMatch(), anyMatch(), noneMatch()
  • reduce()
  • toArray()

Пример:

int[] digits = {0, 1, 2, 3, 4 , 5, 6, 7, 8, 9};
IntStream s = IntStream.of(digits);
long n = s.count();
System.out.println(s.findFirst()); // An exception is thrown

Чтобы использовать поток повторно, его нужно пересоздать:

int[] digits = {0, 1, 2, 3, 4 , 5, 6, 7, 8, 9};
long n = IntStream.of(digits).count();
System.out.println(IntStream.of(digits).findFirst()); // OK

Пример использования нескольких операций:

public class StreamDemo1 {
    public static void main(String[] args) {
        Stream.of("sun", "pool", "beach", "kid", "island", "sea", "sand")
                .map(String::length)
                .filter(i -> i > 3)
                .limit(2)
                .forEach(System.out::println);
    }
}
public class StreamDemo2 {
    public static void main(String[] args) {
        Stream.of("sun", "pool", "beach", "kid", "island", "sea", "sand")
                .map(str -> {
                    System.out.println("Mapping: " + str);
                    return str.length();
                })
                .filter(i -> {
                    System.out.println("Filtering: " + i);
                    return i > 3;
                })
                .limit(2)
                .forEach(System.out::println);
    }
}
public class StreamDemo7 {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("hello", null, "");
        words.stream()
                .filter(t -> t != null && !t.isEmpty())
                .forEach(System.out::println);
    }
}

 

public class StreamDemo8 {
    public static void main(String[] args) {
        List<String> words = Arrays.asList("hello", null, "world","hi");
        words.stream()
                .filter(Objects::nonNull)
                .sorted()
                .distinct()
                .forEach(System.out::println);
    }
}

Полезно использовать Optional для обработки результатов min() и findFirst():

public class StreamDemo3 {
    public static void main(String[] args) {
        List<String> strings = Arrays.asList("Stream",
 "Operations", "on", "Collections");
        Optional<String> optional = strings.stream()
                .min(Comparator.comparing(String::length));
        optional.ifPresent(System.out::println);
    }
}

Операции sum(), average() применимы к числовым потокам:

public class StreamDemo4 {
    public static void main(String[] args) {
        System.out.println(
                IntStream.of(28,4,91,30).sum()
        );
        System.out.println(
                IntStream.of(28,4,91,30).average()
        );
    }
}

Метод collect()

Метод collect() используется для преобразования потока в коллекции (List, Set, Map и др.).

Пример:

public class StreamDemo5 {
    public static void main(String[] args) {
        List<String> phones = new ArrayList<>();
        Collections.addAll(phones, "iPhone 8", "HTC U12", "Huawei Nexus 6P",
                "Samsung Galaxy S9", "LG G6", "Xiaomi MI6", "ASUS Zenfone 2",
                "Sony Xperia Z5", "Meizu Pro 6", "Lenovo S850");

        List<String> filteredPhones = phones.stream()
                .filter(s -> s.length() < 10)
                .collect(Collectors.toList());

        System.out.println(filteredPhones);
    }
}

Преобразование в Map:

public class StreamDemo9 {
    public static void main(String[] args) {
        Stream<ColorBox> stream = Stream.of(new ColorBox(1, 1, 1, "red"),
                new ColorBox(2, 2, 2, "green"),
                new ColorBox(3, 3, 3, "blue"),
                new ColorBox(4, 4, 4, "black"));


        Map<String, ColorBox> map = stream
                .collect(Collectors.toMap(box -> box.color, box -> box));

        map.forEach((k, v) -> System.out.println(k + " " + v));
    }
}

Collectors.groupingBy()

Группировка элементов по признаку:

Map<Integer, List<Integer>> grouped = Stream.of(2, 34, 54, 23)
    .collect(Collectors.groupingBy(i -> i / 10 * 10));

 Подсчет элементов в группах:

Map<Integer, Long> counted = Stream.of(2, 34, 54, 23)
    .collect(Collectors.groupingBy(i -> i / 10 * 10, Collectors.counting()));

2. Intermediate (промежуточные) методы

Промежуточные методы возвращают новый поток, на который можно навешивать следующие операции. Все промежуточные методы являются lazy — они выполняются только после вызова терминальной операции. 

Метод Тип Описание
filter() Stateless Фильтрует элементы по предикату
map() Stateless Преобразует каждый элемент
flatMap() Stateless Плоское отображение вложенных структур
distinct() Stateful Убирает дубликаты
sorted() Stateful Сортирует элементы
limit() Stateful Ограничивает количество
skip() Stateful Пропускает первые n элементов
peek() Stateless Выполняет действие для отладки
parallel() - Делает поток параллельным
sequential() - Делает поток последовательным
unordered() - Убирает порядок

 Stateless vs stateful

Stateless операции — не зависят от других элементов (например, map, filter).

 

Stateful операции — требуют информации о других элементах (например, distinct, sorted).

Пример с filter() и sorted():

words.stream()
     .filter(Objects::nonNull)
     .sorted()
     .distinct()
     .forEach(System.out::println);
Курс 'Java для начинающих' на Udemy Курс 'Java для начинающих' на Udemy
Читайте также:
Комментарии