Java for Beginner
676 subscribers
545 photos
155 videos
12 files
836 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Stream API, внутреннее устройство, особенности и преимущества

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

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

Основные характеристики Stream API:

Декларативный стиль: Позволяет писать код в функциональном стиле, описывая, что нужно сделать, а не как это сделать.
Ленивые вычисления: Операции на потоках выполняются только тогда, когда это действительно необходимо.
Неизменяемость: Потоки не изменяют исходные данные, а создают новые потоки или результаты.
Поддержка параллелизма: Stream API легко интегрируется с параллельными вычислениями, что позволяет улучшить производительность на многоядерных процессорах.


Внутреннее устройство Stream API

Stream API реализован на основе концепции потоков данных (streams), которые представляют собой последовательности элементов из источника данных, такого как коллекция, массив или I/O. Потоки могут быть конечными и бесконечными, что позволяет работать как с ограниченными, так и с неограниченными последовательностями данных.

1. Источник данных
Потоки создаются из различных источников данных, таких как коллекции (List, Set, Map), массивы или генераторы данных. Например, можно создать поток из списка с использованием метода stream():
import java.util.Arrays;
import java.util.List;

public class StreamCreationExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "orange");
items.stream().forEach(System.out::println);
}
}
В этом примере создается поток из списка строк и выводится каждый элемент на консоль.


2. Операции над потоками
Stream API поддерживает два типа операций: промежуточные (intermediate) и терминальные (terminal).

Промежуточные операции: Они применяются к потоку и возвращают новый поток. Примеры: filter(), map(), sorted(). Эти операции ленивы, что означает, что они не выполняются до тех пор, пока не будет вызвана терминальная операция.


Терминальные операции: Эти операции приводят к завершению работы с потоком, производя результат или побочные эффекты. Примеры: collect(), forEach(), reduce().

#Java #Training #Medium #StreamAPI
3. Ленивые вычисления
Одной из ключевых особенностей Stream API является ленивость вычислений. Это означает, что промежуточные операции на потоке не выполняются сразу, а откладываются до тех пор, пока не будет вызвана терминальная операция. Благодаря этому достигается высокая производительность, так как обрабатывается только необходимое количество данных.

4. Параллельные потоки

Stream API предоставляет возможность легко параллелизировать потоковые операции. Это достигается с помощью метода parallelStream(), который разделяет поток на несколько частей, которые обрабатываются параллельно на разных ядрах процессора.
import java.util.Arrays;
import java.util.List;

public class ParallelStreamExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "cherry", "date");

items.parallelStream()
.map(String::toUpperCase)
.forEach(System.out::println);
}
}
В этом примере элементы списка обрабатываются параллельно, что может привести к ускорению выполнения, особенно при работе с большими объемами данных.


Особенности Stream API

1. Неизменяемость
Потоки в Java неизменяемы, что означает, что исходные данные никогда не изменяются. Вместо этого создаются новые потоки или результаты на основе исходных данных. Это делает код более безопасным и предсказуемым, устраняя возможность случайных изменений данных.

2. Ленивость
Как уже упоминалось, ленивые вычисления — это важная особенность Stream API, которая позволяет значительно повысить производительность, особенно при работе с большими объемами данных. Потоковые операции выполняются только тогда, когда это необходимо, что позволяет избежать ненужных вычислений.

3. Параллелизм
Stream API легко интегрируется с параллельными вычислениями, что позволяет более эффективно использовать возможности многоядерных процессоров. Использование параллельных потоков (parallelStream) позволяет обрабатывать данные параллельно, улучшая производительность в многопоточных средах.

Преимущества Stream API

1. Читаемость кода
Stream API значительно улучшает читаемость и лаконичность кода. Вместо использования циклов и условных операторов, разработчики могут использовать потоковые операции, чтобы выразить свою логику декларативно.
import java.util.Arrays;
import java.util.List;

public class ClassicVsStreamExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "cherry", "date");

// Классический подход
for (String item : items) {
if (item.startsWith("a")) {
System.out.println(item.toUpperCase());
}
}

// Подход с использованием Stream API
items.stream()
.filter(item -> item.startsWith("a"))
.map(String::toUpperCase)
.forEach(System.out::println);
}
}
Вывод обоих подходов будет одинаковым, но код с использованием Stream API выглядит более лаконично и понятно.


2. Эффективность
Stream API позволяет обрабатывать данные более эффективно благодаря ленивым вычислениям и поддержке параллелизма. Это особенно важно при работе с большими коллекциями данных, где производительность играет ключевую роль.

3. Легкость параллелизации
Stream API предоставляет простую и интуитивно понятную возможность параллелизации обработки данных. Использование метода parallelStream() не требует сложной настройки многопоточности, что делает параллельные вычисления доступными даже для менее опытных разработчиков.

4. Функциональный подход
Stream API позволяет использовать функциональный стиль программирования, который становится все более популярным. Это позволяет писать код, который более выразителен и легче тестируется, а также способствует лучшей поддержке и масштабируемости.


#Java #Training #Medium #StreamAPI
Основные методы Stream API и примеры использования

1. filter()
Метод filter() используется для фильтрации элементов потока на основе заданного условия. Он принимает предикат (функцию, возвращающую boolean), и если элемент удовлетворяет этому условию, он остается в потоке.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FilterExample {
public static void main(String[] args) {
List<String>```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FilterExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "cherry", "date");

List<String> filteredItems = items.stream()
.filter(item -> item.startsWith("a"))
.collect(Collectors.toList());

System.out.println("Filtered items: " + filteredItems);
}
}
В этом примере фильтруются только те элементы списка, которые начинаются с буквы "a". Результат собирается в новый список с помощью метода collect() и выводится на консоль.


2. map()
Метод map() используется для преобразования элементов потока. Он принимает функцию, которая применяется к каждому элементу потока, и возвращает новый поток с преобразованными элементами.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class MapExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "cherry", "date");

List<String> upperCaseItems = items.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());

System.out.println("Uppercase items: " + upperCaseItems);
}
}
В этом примере все строки из списка преобразуются в верхний регистр, и результат снова собирается в новый список.


3. flatMap()
Метод flatMap() используется для преобразования потоков из элементов, которые сами являются потоками. Этот метод "раскрывает" вложенные потоки, объединяя их в один поток.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FlatMapExample {
public static void main(String[] args) {
List<List<String>> listOfLists = Arrays.asList(
Arrays.asList("apple", "banana"),
Arrays.asList("cherry", "date")
);

List<String> flatList = listOfLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());

System.out.println("Flattened list: " + flatList);
}
}
В этом примере два списка объединяются в один с помощью flatMap(), и результат собирается в новый список.


4. collect()

Метод collect() используется для сбора элементов потока в коллекцию или для других операций агрегирования. Чаще всего используется с Collectors, чтобы собрать элементы в список, множество или другую коллекцию.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CollectExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "cherry", "date");

List<String> collectedItems = items.stream()
.collect(Collectors.toList());

System.out.println("Collected items: " + collectedItems);
}
}


#Java #Training #Medium #StreamAPI
5. reduce()
Метод reduce() используется для агрегирования всех элементов потока в одно значение. Этот метод принимает бинарную операцию, которая сворачивает элементы потока до одного результата.
import java.util.Arrays;
import java.util.List;

public class ReduceExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

int sum = numbers.stream()
.reduce(0, Integer::sum);

System.out.println("Sum: " + sum);
}
}
В этом примере все числа в списке суммируются, и результат выводится на консоль.


6. forEach()
Метод forEach() выполняет действие для каждого элемента потока. Это терминальная операция, которая завершает работу потока, не возвращая результата.
import java.util.Arrays;
import java.util.List;

public class ForEachExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "cherry", "date");

items.stream().forEach(System.out::println);
}
}


7. distinct()
Метод distinct() используется для удаления дубликатов из потока. Он возвращает поток, содержащий только уникальные элементы.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class DistinctExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("apple", "banana", "apple", "date");

List<String> distinctItems = items.stream()
.distinct()
.collect(Collectors.toList());

System.out.println("Distinct items: " + distinctItems);
}
}


8. sorted()
Метод sorted() используется для сортировки элементов потока. Он может принимать компаратор, если требуется нестандартная сортировка.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class SortedExample {
public static void main(String[] args) {
List<String> items = Arrays.asList("banana", "apple", "date", "cherry");

List<String> sortedItems = items.stream()
.sorted()
.collect(Collectors.toList());

System.out.println("Sorted items: " + sortedItems);
}
}


Примеры использования Stream API в реальных задачах

1. Подсчет количества уникальных слов в тексте
Предположим, у нас есть текст, и мы хотим узнать, сколько уникальных слов в нем содержится.
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;

public class UniqueWordsExample {
public static void main(String[] args) {
String text = "apple banana apple cherry date cherry banana";

Set<String> uniqueWords = Arrays.stream(text.split(" "))
.collect(Collectors.toSet());

System.out.println("Unique words: " + uniqueWords);
System.out.println("Number of unique words: " + uniqueWords.size());
}
}


Вывод:
Unique words: [banana, date, cherry, apple]
Number of unique words: 4


#Java #Training #Medium #StreamAPI
2. Фильтрация и преобразование списка объектов
Рассмотрим пример, где у нас есть список объектов Person, и мы хотим отфильтровать людей старше 18 лет, а затем преобразовать их имена в верхний регистр.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

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;
}
}

public class PersonExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 23),
new Person("Bob", 17),
new Person("Charlie", 19)
);

List<String> adultsNames = people.stream()
.filter(person -> person.getAge() > 18)
.map(Person::getName)
.map(String::toUpperCase)
.collect(Collectors.toList());

System.out.println("Adults' names: " + adultsNames);
}
}


Вывод:
Adults' names: [ALICE, CHARLIE]


3. Группировка объектов по ключу
Допустим, нам нужно сгруппировать людей по возрасту.
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

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;
}
}

public class GroupingByExample {
public static void main(String[] args) {
List<Person> people = Arrays.asList(
new Person("Alice", 23),
new Person("Bob", 17),
new Person("Charlie", 23),
new Person("David", 17)
);

Map<Integer, List<Person>> groupedByAge = people.stream()
.collect(Collectors.groupingBy(Person```java
.collect(Collectors.groupingBy(Person::getAge)));

groupedByAge.forEach((age, personList) -> {
System.out.println("Age: " + age);
personList.forEach(person -> System.out.println(" " + person.getName()));
});
}
}
В этом примере люди группируются по возрасту, и результат выводится на консоль. Группировка позволяет удобно структурировать данные по ключевому признаку, что полезно в задачах аналитики и отчетности.


Вывод:
Age: 17
Bob
David
Age: 23
Alice
Charlie


#Java #Training #Medium #StreamAPI