Введение в StreamEx

1. Обзор

Одной из самых интересных функций Java 8 является ссылка:/java-8-streams[ Stream API]- которая, проще говоря, является мощным инструментом для обработки последовательностей элементов.

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

Вот несколько основных функций:

  • Более короткие и удобные способы выполнения повседневных задач

  • 100% совместимость с оригинальным JDK Streams

  • Удобство параллельной обработки: любая новая функция

максимально преимущество на параллельных потоках ** Производительность и минимальные накладные расходы. Если StreamEx позволяет решить

задача, использующая меньше кода по сравнению со стандартным Stream, она не должна быть значительно медленнее обычного (а иногда даже быстрее)

В этом руководстве мы расскажем о некоторых функциях API StreamEx .

** 2. Настройка примера

**

Чтобы использовать StreamEx , нам нужно добавить следующую зависимость в pom.xml :

<dependency>
    <groupId>one.util</groupId>
    <artifactId>streamex</artifactId>
    <version>0.6.5</version>
</dependency>

Последняя версия библиотеки может быть найдена на Maven Центральная .

В этом уроке мы будем использовать простой класс User :

public class User {
    int id;
    String name;
    Role role = new Role();

   //standard getters, setters, and constructors
}

И простой класс Role :

public class Role {
}

** 3. Коллекционные методы ярлыков

**

Одной из самых популярных терминальных операций Streams является операция collect ; это позволяет переупаковывать элементы 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();
  • Операция collect по-прежнему доступна в API, если мы хотим выполнить что-то более сложное, чем брать элементы из Stream и помещать их в коллекцию. **

3.2. Продвинутые Коллекционеры

Другая стенография - groupingBy :

Map<Role, List<User>> role2users = StreamEx.of(users)
  .groupingBy(User::getRole);

Это создаст Map с типом ключа, указанным в ссылке на метод, и будет производить нечто похожее на группу при работе в 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"

Принимая все элементы в Stream a, получается String , объединяющий все из них.

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();

Теперь давайте возьмем массив double элементов в неупорядоченном порядке. Мы хотим создать массив, состоящий из разности между каждой парой.

Мы можем использовать метод pairMap для выполнения этой операции:

public double[]getDiffBetweenPairs(double... numbers) {
    return DoubleStreamEx.of(numbers)
      .pairMap((a, b) -> b - a)
      .toArray();
}

6. Операции с картой

6.1. Фильтрация по ключам

Еще одна полезная функция - возможность создавать Stream из Map и фильтровать элементы, используя значения, на которые они указывают.

В этом случае мы принимаем все ненулевые значения:

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 принимает Map и преобразует его в Stream объектов значения ключа. Затем мы используем операцию flatMapValues для преобразования нашего списка ролей в Stream отдельных значений.

Затем мы можем инвертировать пару ключ-значение, сделав класс User ключом, а класс Role - значением.

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

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 и его различных утилитах. Нам предстоит еще многое сделать - и у них есть удобный шпаргалка here .

Как всегда, полный исходный код доступен на over на GitHub .