stream api and lambda in java

Stream API и лямбда выражения в Java.

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

Использование λ–выражения в Java дает возможность программисту внедрять функциональное программирование в парадигме объектно-ориентированного. Лямбда выражения -позволяют писать быстрее и делают код более ясным. В мире Java они появились в 8 версии и не остались незамеченными опытными программистами. Их очень хорошо применять Stream API.

Еще немного теории о лямбда выражениях:

  • Лямбда-выражение является блоком кода с параметрами;
  • Используйте лямбда-выражение,когда хотите выполнить блок кода в более поздний момент времени;
  • Лямбда-выражения могут быть преобразованы в функциональные интерфейсы;
  • Лямбда-выражения имеют доступ к final переменным из охватывающей области видимости;
  • Ссылки на метод и конструктор ссылаются на методы или конструкторы без их вызова;
  • Теперь вы можете добавить методы по умолчанию и статические методы к интерфейсам,которые обеспечивают конкретные реализации;
  • Вы должны разрешать любые конфликты между методами по умолчанию из нескольких интерфейсов;

Для Stream есть два режима: последовательный и параллельный. Это позволяет задействовать возможности многоядерных процессоров. Коллекции используют fork/join параллелизм для разбиения работы на части.

Для того, чтобы была возможность работать со стрим API нужно выполнить такой алгоритм:

  • создать stream;
  • выполнить цепочку операций с объектами stream;
  • выполнить терминальную операцию(объединение результата в коллекцию, агрегатная функция и т.д).

Создать стрим можно несколькими способами. Самые популярные из них будут в примерах:

  • collection.stream()…
  • Stream.of(значение1,… значениеN)…
  • Arrays.stream(массив)…
  • Files.lines(путь_к_файлу)…
  • «строка».chars()…
  • Stream.builder().add(…)….build()…
  • collection.parallelStream()…
  • Stream.iterate(начальное_условие, выражение_генерации)…
  • Stream.generate(выражение_генерации)…

Пример: 

Код    
  1. package com.lambda;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.Arrays;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.stream.Stream;
  9.  
  10. public class LambdaAndStreamAPI {
  11.     static Map<Integer, String> map = new HashMap<>();
  12.     static {
  13.         map.put(1, "User1");
  14.         map.put(2, "User2");
  15.         map.put(3, "User3");
  16.     }
  17.  
  18.     public static void main(String[] args) {
  19.         map.forEach((k, v) -> System.out.println(k + " " + v));//обход мапы уже не такой громоздкий
  20.        
  21.         List<String> list = new ArrayList<>();
  22.         list.stream().filter("a"::equals);//одно и то же
  23.         Stream.of(list).filter("a"::equals);//просто разные способы
  24.        
  25.         System.out.println("abc4".chars().count());//вот, как можно быстро работать со строками
  26.        
  27.         int[] array = {3,5};
  28.         System.out.println(Arrays.stream(array).average());//для масивов не нужно писать отдельных методов
  29.     }
  30.  
  31. }

 

В примере выше мы просто рассмотрели, как можно вызвать работу стрим API. У него очень много полезных методов, которые могут заменить написание громоздкого кода. Например, теперь чтобы пройтись по мапе нужно написать всего одну строчку кода:  map.forEach((k, v) -> System.out.println(k + «=» + v));

Вот метод, который удаляет элемент списка

public List<Integer> removeEl(List<Integer> list, Integer el) {
list.removeIf(i -> i.equals(el));
return list;
}

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

  1. filter —  отфильтровывает записи, возвращает только записи, соответствующие условию. Пример: collection.stream().filter(«a1»::equals).count();
  2. skipa — позволяет пропустить N первых элементов. Пример: list.stream().skip(list.size() -1).findFirst().orElse(«1»);
  3. distinct — возвращает стрим без дубликатов (для метода equals). Пример: collection.stream().distinct().collect(Collectors.toList());
  4. map — преобразует каждый элемент стрим. Пример: collection.stream().map((s) -> s +»_1″).collect(Collectors.toList());
  5. peek — возвращает тот же стрим, но применяет функцию к каждому его элементу. Пример: collection.stream().map(String::toUpperCase).peek((e) -> System.out.print(«,» + e)).collect(Collectors.toList());
  6. limit — позволяет ограничить выборку определенным количеством первых элементов. Пример: collection.stream().limit(2).collect(Collectors.toList());
  7. sorted — позволяет сортировать значения либо в натуральном порядке, либо задавая Comparator. Пример: collection.stream().sorted().collect(Collectors.toList());
  8. mapToInt, mapToDouble, mapToLong — аналог map, но возвращает числовой стрим (то есть стрим из числовых примитивов). Пример: collection.stream().mapToInt((s) -> Integer.parseInt(s)).toArray();
  9. flatMap, flatMapToInt, flatMapToDouble, flatMapToLong — похоже на map, но может создавать из одного элемента несколько. Пример: collection.stream().flatMap((p) ->
    Arrays.asList(p.split(«,»)).stream()).toArray(String[]::new);
  10. findFirst — возвращает первый элемент из стрима (возвращает Optional). Пример: collection.stream().findFirst().orElse(«1»);
  11. findAny — возвращает любой подходящий элемент из стрим (возвращает Optional). Пример: collection.stream().findAny().orElse(«1»);
  12. collect — представление результатов в виде коллекций и других структур данных. Пример: collection.stream().filter((s) -> s.contains(«1»)).collect(Collectors.toList());
  13. count — возвращает количество элементов. Пример: collection.stream().filter(«a1»::equals).count();
  14. anyMatch — возвращает true, если условие выполняется хотя бы для одного элемента. Пример: collection.stream().anyMatch(«a1»::equals);
  15. noneMatch — возвращает true, если условие не выполняется ни для одного элемента. Пример: collection.stream().noneMatch(«a8»::equals);
  16. allMatch — возвращает true, если условие выполняется для всех элементов. Пример: collection.stream().allMatch((s) -> s.contains(«1»));
  17. min — возвращает минимальный элемент, в качестве условия использует компаратор. Пример: collection.stream().min(String::compareTo).get();
  18. max — возвращает максимальный элемент, в качестве условия использует компаратор. Пример: collection.stream().max(String::compareTo).get();
  19. forEach — применяет функцию к каждому объекту стрима, порядок при параллельном выполнении не гарантируется. Пример: set.stream().forEach((p) -> p.append(«_1»));
  20. forEachOrdered — применяет функцию к каждому объекту стрим, сохранение порядка элементов гарантирует. Пример: list.stream().forEachOrdered((p) -> p.append(«_new»));
  21. toArray — возвращает массив значений стрим. Пример: collection.stream().map(String::toUpperCase).toArray(String[]::new);
  22. reduce — позволяет выполнять агрегатные функции на всей коллекцией и возвращать один результат. Пример: collection.stream().reduce((s1, s2) -> s1 + s2).orElse(0);
  23. sum — возвращает сумму всех чисел. Пример: collection.stream().mapToInt((s) -> Integer.parseInt(s)).sum();
  24. average — возвращает среднее арифметическое всех чисел. Пример: collection.stream().mapToInt((s) -> Integer.parseInt(s)).average();
  25. mapToObj — преобразует числовой стрим обратно в объектный. Пример: intStream.mapToObj((id) -> new Key(id)).toArray();
  26. isParallel — узнать является ли Stream параллельным parallel вернуть параллельный стрим, если он уже параллельный, то может вернуть самого себя;
  27. sequential — вернуть последовательный стрим, если он уже последовательный, то может вернуть самого себя.

Теперь реальные жизненные ситуации использования Stream API и лямбда выражений.

Для этого я создал класс User с набором стандартных полей пользователя и заполнил коллекции тестовыми юзерами:

Код    
  1. /**
  2.  * обычный класс пользователь с конструктором по умолчанию
  3.  * сгенерированными методами equals, hashcode
  4.  * @author admin
  5.  *
  6.  */
  7. public class User {
  8.  
  9.     private int id;
  10.     private String name;
  11.     private int age;
  12.     private String sex;
  13.    
  14.     public User() {
  15.         // TODO Auto-generated constructor stub
  16.     }
  17.    
  18.    
  19.  
  20.     public User(int id, String name, int age, String sex) {
  21.         super();
  22.         this.id = id;
  23.         this.name = name;
  24.         this.age = age;
  25.         this.sex = sex;
  26.     }
  27.  
  28.  
  29.  
  30.     public int getId() {
  31.         return id;
  32.     }
  33.  
  34.     public void setId(int id) {
  35.         this.id = id;
  36.     }
  37.  
  38.     public String getName() {
  39.         return name;
  40.     }
  41.  
  42.     public void setName(String name) {
  43.         this.name = name;
  44.     }
  45.  
  46.     public int getAge() {
  47.         return age;
  48.     }
  49.  
  50.     public void setAge(int age) {
  51.         this.age = age;
  52.     }
  53.  
  54.     public String getSex() {
  55.         return sex;
  56.     }
  57.  
  58.     public void setSex(String sex) {
  59.         this.sex = sex;
  60.     }
  61.  
  62.    
  63.  
  64.  
  65.     @Override
  66.     public int hashCode() {
  67.         final int prime = 31;
  68.         int result = 1;
  69.         result = prime * result + age;
  70.         result = prime * result + id;
  71.         result = prime * result + ((name == null) ? 0 : name.hashCode());
  72.         result = prime * result + ((sex == null) ? 0 : sex.hashCode());
  73.         return result;
  74.     }
  75.  
  76.  
  77.  
  78.     @Override
  79.     public boolean equals(Object obj) {
  80.         if (this == obj)
  81.             return true;
  82.         if (obj == null)
  83.             return false;
  84.         if (getClass() != obj.getClass())
  85.             return false;
  86.         User other = (User) obj;
  87.         if (age != other.age)
  88.             return false;
  89.         if (id != other.id)
  90.             return false;
  91.         if (name == null) {
  92.             if (other.name != null)
  93.                 return false;
  94.         } else if (!name.equals(other.name))
  95.             return false;
  96.         if (sex == null) {
  97.             if (other.sex != null)
  98.                 return false;
  99.         } else if (!sex.equals(other.sex))
  100.             return false;
  101.         return true;
  102.     }
  103.  
  104.  
  105.  
  106.     @Override
  107.     public String toString() {
  108.         return "User [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + "]";
  109.     }
  110.    
  111.    
  112.    
  113.    
  114. }
Код    
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.stream.Collectors;
  4.  
  5. public class Main {
  6.  
  7.     private static List<User> users = new ArrayList<>();
  8.  
  9.     static {// создаем наших юзеров и заполняем ими коллекцию
  10.         User user1 = new User(1, "Ivan", 23, "man");
  11.         User user2 = new User(2, "Olga", 45, "woman");
  12.         User user3 = new User(3, "John", 12, "man");
  13.         User user4 = new User(4, "Vitaliy", 25, "man");
  14.         users.add(user1);
  15.         users.add(user2);
  16.         users.add(user3);
  17.         users.add(user4);
  18.     }
  19.  
  20.     /**
  21.      * Что если мы хотим получить данные по некоторым условиям?
  22.      * Не проблема: используем функцию filter и возвращаем коллекцию
  23.      * @param bottomAge
  24.      * @param topAge
  25.      * @param sex
  26.      * @return
  27.      */
  28.     public static List<User> getUsersByAgeRangeAndSex(int bottomAge, int topAge, String sex) {
  29.         List<User> usersByAgeAndSex = users.stream()
  30.                 .filter((p) -> p.getAge() > bottomAge && p.getAge() < topAge && p.getSex().equals(sex))
  31.                 .collect(Collectors.toList());
  32.         return usersByAgeAndSex;
  33.     }
  34.  
  35.     /**
  36.      * Функция average(), совместно с filter позволяют нам
  37.      * получить среднее значение по некоторым полям и по фильтрам в одну строчку
  38.      * @return
  39.      */
  40.     public static double getMensAverage() {
  41.         return users.stream().filter((p) -> p.getSex().equals("man")).mapToInt(User::getAge).average().getAsDouble();
  42.     }
  43.  
  44.     /**
  45.      * Если нужно использовать несколько фильтров, можете смело это делать
  46.      * можно добавить сколько условий, сколько этого требует задача
  47.      * @return
  48.      */
  49.     public static int findCountOfWorkingPeople() {
  50.         return (int) users.stream().filter((p) -> p.getAge() >= 18).filter(
  51.                 (p) -> (p.getSex().equals("woman") && p.getAge() < 55) || (p.getSex().equals("man") && p.getAge() < 60))
  52.                 .count();
  53.     }
  54.  
  55.     /**
  56.      * Удалить елемент из списка можно в одну строчку безо всякого труда
  57.      * @param user
  58.      * @return
  59.      */
  60.     public static List<User> removeUser(User user) {
  61.         users.removeIf(i -> i.equals(user));
  62.         return users;
  63.     }
  64.  
  65.     public static void main(String[] args) {
  66.         System.out.println(getUsersByAgeRangeAndSex(18, 30, "man"));
  67.         System.out.println(getMensAverage());
  68.         System.out.println(findCountOfWorkingPeople());
  69.         System.out.println(removeUser(users.get(1)));
  70.  
  71.     }
  72.  
  73. }

Эти примеры — только малая часть, что можно делать с помощью лямбда выражений и Stream API. Я советую Вам посмотреть все методы и запомнить для себя наиболее полезные на Ваш взгляд. Например, как удаление элемента из списка.

На этом, думаю, пора заканчивать. Не забудьте поделиться этой статьей с друзьями и знакомыми (если, конечно, она Вам понравилась).

Добавить комментарий