Руководство Java Stream
1. Stream
Java 8 вводит новую концепцию под названием Stream (поток). Когда вы впервые читаете о Stream API, вы можете быть сбиты с толку, потому что его название похоже на InputStream и OutputStream. Но Java 8 Stream - это нечто совершенно другое. Stream - это монада (monad), поэтому он играет важную роль в внедрении функционального программирования (functional programming) в Java.
Прежде чем начать эту статью, я рекомендую вам ознакомиться с functional interface и некоторыми распространенными functional interface, такими как: Supplier, Consumer, Predicate. Ниже приведены мои статьи:
В функциональном программировании (functional programming), монада (monad) -это структура, представляющая собой вычисление (computation), требующее последовательности связанных шагов. Для простоты смотрите пример монады ниже:
List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList
.stream() // (1) return a Stream
.filter(s -> s.startsWith("c")) // (2) return a new Stream
.map(String::toUpperCase) // (3) return a new Stream
.sorted() // (4) return a new Stream
.forEach(System.out::println); // (5)
Output:
C1
C2
- Создать Stream из объекта List.
- Создать новый Stream из предыдущего Stream, который включает только элементы, начинающиеся с буквы "с".
- Создать новый Stream из предыдущего Stream со всеми элементами, преобразованными в заглавные (upercase).
- Создать новый Stream из предыдущего Stream, отсортировав элементы.
- Распечатать элементы последнего Stream.
В предыдущем примере, шаги (2) - (4) являются промежуточными операциями (intermediate operation), поскольку они возвращают объект Stream. Таким образом, вы можете вызвать другой метод Stream без необходимости заканчивать его точкой с запятой.
Терминальная операция (terminal operation) - это метод, который возвращает void или возвращает другой тип с Stream. В предыдущем примере шаг 5 является терминальной операцией, поскольку метод Stream.forEach возвращает void.
Ниже приведены характеристики и преимущества Java 8 Stream:
- Никакого хранения. Stream - это не структура данных, а только представление источника данных (который может быть массивом, списком илиI/O Channel и т.д).
- Stream функциональен по своей природе. Любые изменения с Stream не изменят источники данных. Например, фильтрация Stream не приведет к удалению каких-либо элементов, а создаст новый Stream, включающий отфильтрованные элементы.
- Ленивое исполнение. Операции над Stream будут выполняться не сразу. Они будут выполняться только тогда, когда пользователям действительно нужны результаты.
- Расходуемые (Consumable). Доступ к элементам Stream существляется только один раз за время существования Stream. После утверждения, Stream становится недействительным, как и Iterator. Вы должны восстановить новый Stream, если хотите снова посмотреть Stream.
Смотрите иллюстрацию ниже, чтобы лучше понять, как работает Stream.
- Создайте stream из коллекции (collection).
- Фильтровать цвета, отличные от красного.
- Нарисовать треугольники розовой краской.
- Фильтровать формы, которые не являются квадратными.
- Вычислить общую площадь.
Класс Employee участвует в нескольких примерах из этой статьи:
Employee.java
package org.o7planning.stream.ex;
public class Employee {
private String name;
private float salary;
private String gender; // "M", "F"
public Employee(String name, float salary, String gender) {
this.name = name;
this.salary = salary;
this.gender = gender;
}
public String getName() {
return name;
}
public float getSalary() {
return salary;
}
public String getGender() {
return gender;
}
public boolean isFemale() {
return "F".equals(this.getGender());
}
}
2. Stream.filter(Predicate)
Возвращает Stream, состоящий из элементов этого Stream, соответствующих заданному Predicate.
Stream<T> filter(Predicate<? super T> predicate)
Например: Из списка сотрудников (Employee) нужно распечатать список сотрудниц с зарплатой более 2500.
Stream_filter_ex1.java
package org.o7planning.stream.ex;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Stream_filter_ex1 {
public static void main(String[] args) {
Employee john = new Employee("John P.", 1500, "M");
Employee sarah = new Employee("Sarah M.", 2000, "F");
Employee charles = new Employee("Charles B.", 1700, "M");
Employee mary = new Employee("Mary T.", 5000, "F");
Employee sophia = new Employee("Sophia B.", 7000, "F");
List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);
// Employee is Female and salary > 2500
Predicate<Employee> predicate = e -> e.isFemale() && e.getSalary() > 2500;
employees //
.stream() //
.filter(predicate) //
.forEach(e -> System.out.println(e.getName()+ " : " + e.getSalary()));
}
}
Output:
Mary T. : 5000.0
Sophia B. : 7000.0
Если нестатический метод (non-static method), не имеет параметров и возвращает значение boolean, то его ссылка считается Predicate. (См. объяснение в моей статье о Java Predicate).
Например: Создать Predicate из ссылки метода (method reference):
Predicate<Employee> p = Employee::isFemale;
// Same as:
Predicate<Employee> p = employee -> employee.isFemale();
Stream_filter_ex2.java
package org.o7planning.stream.ex;
import java.util.Arrays;
import java.util.List;
public class Stream_filter_ex2 {
public static void main(String[] args) {
Employee john = new Employee("John P.", 1500, "M");
Employee sarah = new Employee("Sarah M.", 2000, "F");
Employee charles = new Employee("Charles B.", 1700, "M");
Employee mary = new Employee("Mary T.", 5000, "F");
Employee sophia = new Employee("Sophia B.", 7000, "F");
List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);
employees //
.stream() //
.filter(Employee::isFemale) //
.forEach(e -> System.out.println(e.getName()+ " : " + e.getSalary()));
}
}
Output:
Sarah M. : 2000.0
Mary T. : 5000.0
Sophia B. : 7000.0
3. Stream.sorted(Comparator)
Возвращает Stream, состоящий из элементов этого потока, отсортированных в соответствии с предоставленным Comparator.
Stream<T> sorted(Comparator<? super T> comparator)
- Руководство Java Comparator
Например: Сортировка сотрудников в порядке возрастания заработной платы:
Stream_sort_ex1.java
package org.o7planning.stream.ex;
import java.util.Arrays;
import java.util.List;
public class Stream_sort_ex1 {
public static void main(String[] args) {
Employee john = new Employee("John P.", 1500, "M");
Employee sarah = new Employee("Sarah M.", 2000, "F");
Employee charles = new Employee("Charles B.", 1700, "M");
Employee mary = new Employee("Mary T.", 5000, "F");
Employee sophia = new Employee("Sophia B.", 7000, "F");
List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);
employees //
.stream() //
.sorted (
(e1,e2) -> (int) (e1.getSalary() - e2.getSalary())
) //
.forEach(e -> System.out.println(e.getSalary() + " : " + e.getName()));
}
}
Output:
1500.0 : John P.
1700.0 : Charles B.
2000.0 : Sarah M.
5000.0 : Mary T.
7000.0 : Sophia B.
Например: Сортировка сотрудника по полу (Gender) и зарплате:
Stream_sort_ex2.java
package org.o7planning.stream.ex;
import java.util.Arrays;
import java.util.List;
public class Stream_sort_ex2 {
public static void main(String[] args) {
Employee john = new Employee("John P.", 1500, "M");
Employee sarah = new Employee("Sarah M.", 2000, "F");
Employee charles = new Employee("Charles B.", 1700, "M");
Employee mary = new Employee("Mary T.", 5000, "F");
Employee sophia = new Employee("Sophia B.", 7000, "F");
List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);
employees //
.stream() //
.sorted (
(e1,e2) -> {
int v = e1.getGender().compareTo(e2.getGender());
if(v == 0) {
v = (int) (e1.getSalary() - e2.getSalary());
}
return v;
}
) //
.forEach(e -> System.out.println(e.getGender()+ " : "+ e.getSalary() + " : " + e.getName()));
}
}
Output:
F : 2000.0 : Sarah M.
F : 5000.0 : Mary T.
F : 7000.0 : Sophia B.
M : 1500.0 : John P.
M : 1700.0 : Charles B.
4. Stream.map(Function)
Возвращает новый Stream, состоящий из результатов применения данной Function к элементам этого Stream.
<R> Stream<R> map(Function<? super T,? extends R> mapper)
Например: преобразует список String в заглавные (uppercase).
Stream_map_ex1.java
package org.o7planning.stream.ex;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Stream_map_ex1 {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c", "d", "e");
List<String> newList = list //
.stream() // a Stream
.map(s -> s.toUpperCase()) // a new Stream
.collect(Collectors.toList()); // Stream => List
System.out.println(list); // [a, b, c, d, e]
System.out.println(newList); // [A, B, C, D, E]
}
}
Если нестатический метод (non-static method), не имеет параметров и возвращает значение, то его ссылка рассматривается как Function. (См. Более подробное объяснение в моей статье о Java Function).
// Create a Function from a method reference:
Function<String, String> f1 = String::toUpperCase;
// Same as:
Function<String, String> f2 = s -> s.toUpperCase();
Stream_map_ex2.java
package org.o7planning.stream.ex;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Stream_map_ex2 {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c", "d", "e");
List<String> newList = list //
.stream() // a Stream
.map(String::toUpperCase) // a new Stream
.collect(Collectors.toList()); // Stream => List
System.out.println(list); // [a, b, c, d, e]
System.out.println(newList); // [A, B, C, D, E]
}
}
Например: Двойная зарплата для каждого сотрудника в списке:
Stream_map_ex3.java
package org.o7planning.stream.ex;
import java.util.Arrays;
import java.util.List;
public class Stream_map_ex3 {
public static void main(String[] args) {
Employee john = new Employee("John P.", 1500, "M");
Employee sarah = new Employee("Sarah M.", 2000, "F");
Employee charles = new Employee("Charles B.", 1700, "M");
Employee mary = new Employee("Mary T.", 5000, "F");
Employee sophia = new Employee("Sophia B.", 7000, "F");
List<Employee> employees = Arrays.asList(john, sarah, charles, mary, sophia);
employees //
.stream() // a Stream.
.map((e) -> new Employee(e.getName(), e.getSalary()* 2, e.getGender())) // a new Stream.
.forEach(c -> System.out.println(c.getName()+ " : " + c.getSalary()));
}
}
Output:
John P. : 3000.0
Sarah M. : 4000.0
Charles B. : 3400.0
Mary T. : 10000.0
Sophia B. : 14000.0
Java Basic
- Настройте java compiler для обработки вашего Annotation (Annotation Processing Tool)
- Программирование на Java для группы с помощью Eclipse и SVN
- Руководство Java WeakReference
- Руководство Java PhantomReference
- Сжатие и декомпрессия в Java
- Настройка Eclipse для использования JDK вместо JRE
- Методы String.format() и printf() в Java
- Синтаксис и новые функции в Java 8
- Регулярные выражения Java
- Руководство Java Multithreading Programming
- Библиотеки Java JDBC Driver для различных типов баз данных
- Руководство Java JDBC
- Получить значения столбцов, автоматически возрастающих при вставлении (Insert) записи, используя JDBC
- Руководство Java Stream
- Руководство Java Functional Interface
- Введение в Raspberry Pi
- Руководство Java Predicate
- Абстрактный класс и Interface в Java
- Модификатор доступа (Access modifiers) в Java
- Руководство Java Enum
- Руководство Java Annotation
- Сравнение и Сортировка в Java
- Руководство Java String, StringBuffer и StringBuilder
- Обработка исключений Java - Java Exception Handling
- Руководство Java Generics
- Манипулирование файлами и каталогами в Java
- Руководство Java BiPredicate
- Руководство Java Consumer
- Руководство Java BiConsumer
- Что мне нужно для начала работы с Java?
- История Java и разница между Oracle JDK и OpenJDK
- Установить Java в Windows
- Установите Java в Ubuntu
- Установите OpenJDK в Ubuntu
- Установить Eclipse
- Установите Eclipse в Ubuntu
- Быстрое изучение Java для начинающих
- История бит и байтов в информатике
- Типы данных в java
- Битовые операции
- Команда if else в Java
- команды switch в Java
- Циклы в Java
- Массивы (Array) в Java
- JDK Javadoc в формате CHM
- Наследование и полиморфизм в Java
- Руководство Java Function
- Руководство Java BiFunction
- Пример Java encoding и decoding с использованием Apache Base64
- Руководство Java Reflection
- Java Удаленный вызов методов - Java RMI
- Руководство Программирование Java Socket
- Какую платформу я должен выбрать для разработки приложений Java Desktop?
- Руководство Java Commons IO
- Руководство Java Commons Email
- Руководство Java Commons Logging
- Понимание Java System.identityHashCode, Object.hashCode и Object.equals
- Руководство Java SoftReference
- Руководство Java Supplier
- Аспектно-ориентированное программирование Java с помощью AspectJ (AOP)
Show More
- Руководства Java Servlet/JSP
- Руководства Java Collections Framework
- Java API для HTML, XML
- Руководства Java IO
- Руководства Java Date Time
- Руководства Spring Boot
- Руководства Maven
- Руководства Gradle
- Руководства Java Web Services
- Руководства Java SWT
- Руководства JavaFX
- Руководства Oracle Java ADF
- Руководства Struts2 Framework
- Руководства Spring Cloud