1. Обзор
Одной из самых захватывающих функций Java 8 является Stream
API , который, проще говоря, представляет собой мощный инструмент для обработки последовательностей элементов.
StreamEx
— это библиотека, которая предоставляет дополнительные функции для стандартного Stream API, а также повышает производительность.
Вот несколько основных особенностей:
- Более короткие и удобные способы выполнения повседневных задач
- 100% совместимость с оригинальными
потоками JDK.
- Удобство для параллельной обработки: любая новая функция максимально использует преимущества параллельных потоков.
- Производительность и минимальные накладные расходы. Если
StreamEx
позволяет решить задачу с использованием меньшего количества кода по сравнению со стандартнымStream,
он не должен быть значительно медленнее обычного (а иногда даже быстрее)
В этом руководстве мы представим некоторые функции StreamEx
API.
2. Настройка примера
Чтобы использовать StreamEx
, нам нужно добавить следующую зависимость в pom.xml
:
<dependency>
<groupId>one.util</groupId>
<artifactId>streamex</artifactId>
<version>0.6.5</version>
</dependency>
Последнюю версию библиотеки можно найти на Maven Central .
В этом руководстве мы будем использовать простой класс User :
public class User {
int id;
String name;
Role role = new Role();
// standard getters, setters, and constructors
}
И простой класс ролей :
public class Role {
}
3. Методы быстрого доступа к коллекторам
Одной из самых популярных терминальных операций Streams
является операция сбора
; это позволяет переупаковывать элементы Stream
в коллекцию по нашему выбору.
Проблема в том, что код может стать излишне многословным для простых сценариев:
users.stream()
.map(User::getName)
.collect(Collectors.toList());
3.1. Сбор в коллекцию
Теперь, с StreamEx, нам не нужно предоставлять Collector
, чтобы указать, что нам нужен List
, Set, Map, InmutableList и
т. д .:
List<String> userNames = StreamEx.of(users)
.map(User::getName)
.toList();
Операция сбора
по-прежнему доступна в API, если мы хотим выполнить что-то более сложное, чем брать элементы из потока
и помещать их в коллекцию.
3.2. Продвинутые коллекционеры
Другое сокращение — groupingBy
:
Map<Role, List<User>> role2users = StreamEx.of(users)
.groupingBy(User::getRole);
Это создаст карту
с типом ключа, указанным в ссылке на метод, создавая что-то похожее на группу с помощью операции в SQL.
Используя простой Stream
API, нам нужно написать:
Map<Role, List<User>> role2users = users.stream()
.collect(Collectors.groupingBy(User::getRole));
Аналогичную сокращенную форму можно найти для Collectors.joining():
StreamEx.of(1, 2, 3)
.joining("; "); // "1; 2; 3"
Который принимает все элементы в потоке
, a создает строку
, объединяющую их все.
4. Добавление, удаление и выбор элементов
В некоторых сценариях у нас есть список объектов разных типов, и нам нужно отфильтровать их по типу:
List usersAndRoles = Arrays.asList(new User(), new Role());
List<Role> roles = StreamEx.of(usersAndRoles)
.select(Role.class)
.toList();
Мы можем добавлять элементы в начало или конец нашего Stream с
помощью следующих удобных операций:
List<String> appendedUsers = StreamEx.of(users)
.map(User::getName)
.prepend("(none)")
.append("LAST")
.toList();
Мы можем удалить ненужные нулевые элементы с помощью nonNull()
и использовать Stream
как Iterable
:
for (String line : StreamEx.of(users).map(User::getName).nonNull()) {
System.out.println(line);
}
5. Поддержка математических операций и примитивных типов
StreamEx
добавляет поддержку примитивных типов, как мы можем видеть в этом самоочевидном примере:
short[] src = {1,2,3};
char[] output = IntStreamEx.of(src)
.map(x -> x * 5)
.toCharArray();
Теперь давайте возьмем массив двойных
элементов в неупорядоченном виде. Мы хотим создать массив, состоящий из разницы между каждой парой.
Мы можем использовать метод pairMap
для выполнения этой операции:
public double[] getDiffBetweenPairs(double... numbers) {
return DoubleStreamEx.of(numbers)
.pairMap((a, b) -> b - a)
.toArray();
}
6. Операции с картой
6.1. Фильтрация по ключам
Еще одна полезная функция — возможность создать поток
из карты
и отфильтровать элементы, используя значения, на которые они указывают.
В этом случае мы берем все ненулевые значения:
Map<String, Role> nameToRole = new HashMap<>();
nameToRole.put("first", new Role());
nameToRole.put("second", null);
Set<String> nonNullRoles = StreamEx.ofKeys(nameToRole, Objects::nonNull)
.toSet();
6.2. Работа с парами ключ-значение
Мы также можем работать с парами ключ-значение, создав экземпляр EntryStream
:
public Map<User, List<Role>> transformMap(
Map<Role, List<User>> role2users) {
Map<User, List<Role>> users2roles = EntryStream.of(role2users)
.flatMapValues(List::stream)
.invert()
.grouping();
return users2roles;
}
Специальная операция EntryStream.of
берет карту
и преобразует ее в поток
объектов ключ-значение. Затем мы используем операцию flatMapValues
, чтобы преобразовать наш список ролей в поток
отдельных значений.
Затем мы можем инвертировать
пару ключ-значение, сделав класс User
ключом, а класс Role
значением.
И, наконец, мы можем использовать операцию группировки
, чтобы преобразовать нашу карту в инверсию полученной, всего за четыре операции.
6.3. Сопоставление ключ-значение
Мы также можем сопоставлять ключи и значения независимо друг от друга:
Map<String, String> mapToString = EntryStream.of(users2roles)
.mapKeys(String::valueOf)
.mapValues(String::valueOf)
.toMap();
Благодаря этому мы можем быстро преобразовать наши ключи или значения в другой требуемый тип.
7. Файловые операции
Используя StreamEx
, мы можем эффективно читать файлы, т. е. не загружая сразу полные файлы. Это удобно при обработке больших файлов:
StreamEx.ofLines(reader)
.remove(String::isEmpty)
.forEach(System.out::println);
Обратите внимание, что мы использовали метод remove()
для фильтрации пустых строк.
Обратите внимание, что StreamEx
не закроет файл автоматически. Следовательно, мы должны помнить о ручном выполнении операции закрытия как при чтении файла, так и при записи, чтобы избежать ненужных накладных расходов памяти.
8. Заключение
В этом уроке мы узнали о StreamEx
и его различных утилитах. Еще многое предстоит пройти — и здесь у них есть удобная шпаргалка .
Как всегда, полный исходный код доступен на GitHub .