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

stream api and lambda in 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(выражение_генерации)…

Пример: 

Код   
package com.lambda;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

public class LambdaAndStreamAPI {
    static Map<Integer, String> map = new HashMap<>();
    static {
        map.put(1, "User1");
        map.put(2, "User2");
        map.put(3, "User3");
    }

    public static void main(String[] args) {
        map.forEach((k, v) -> System.out.println(k + " " + v));//обход мапы уже не такой громоздкий
       
        List<String> list = new ArrayList<>();
        list.stream().filter("a"::equals);//одно и то же
        Stream.of(list).filter("a"::equals);//просто разные способы
       
        System.out.println("abc4".chars().count());//вот, как можно быстро работать со строками
       
        int[] array = {3,5};
        System.out.println(Arrays.stream(array).average());//для масивов не нужно писать отдельных методов
    }

}

 

В примере выше мы просто рассмотрели, как можно вызвать работу стрим 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 с набором стандартных полей пользователя и заполнил коллекции тестовыми юзерами:

Код   
/**
 * обычный класс пользователь с конструктором по умолчанию
 * сгенерированными методами equals, hashcode
 * @author admin
 *
 */

public class User {

    private int id;
    private String name;
    private int age;
    private String sex;
   
    public User() {
        // TODO Auto-generated constructor stub
    }
   
   

    public User(int id, String name, int age, String sex) {
        super();
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }



    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

   


    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((sex == null) ? 0 : sex.hashCode());
        return result;
    }



    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (age != other.age)
            return false;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (sex == null) {
            if (other.sex != null)
                return false;
        } else if (!sex.equals(other.sex))
            return false;
        return true;
    }



    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", age=" + age + ", sex=" + sex + "]";
    }
   
   
   
   
}
Код   
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class Main {

    private static List<User> users = new ArrayList<>();

    static {// создаем наших юзеров и заполняем ими коллекцию
        User user1 = new User(1, "Ivan", 23, "man");
        User user2 = new User(2, "Olga", 45, "woman");
        User user3 = new User(3, "John", 12, "man");
        User user4 = new User(4, "Vitaliy", 25, "man");
        users.add(user1);
        users.add(user2);
        users.add(user3);
        users.add(user4);
    }

    /**
     * Что если мы хотим получить данные по некоторым условиям?
     * Не проблема: используем функцию filter и возвращаем коллекцию
     * @param bottomAge
     * @param topAge
     * @param sex
     * @return
     */

    public static List<User> getUsersByAgeRangeAndSex(int bottomAge, int topAge, String sex) {
        List<User> usersByAgeAndSex = users.stream()
                .filter((p) -> p.getAge() > bottomAge && p.getAge() < topAge && p.getSex().equals(sex))
                .collect(Collectors.toList());
        return usersByAgeAndSex;
    }

    /**
     * Функция average(), совместно с filter позволяют нам
     * получить среднее значение по некоторым полям и по фильтрам в одну строчку
     * @return
     */

    public static double getMensAverage() {
        return users.stream().filter((p) -> p.getSex().equals("man")).mapToInt(User::getAge).average().getAsDouble();
    }

    /**
     * Если нужно использовать несколько фильтров, можете смело это делать
     * можно добавить сколько условий, сколько этого требует задача
     * @return
     */

    public static int findCountOfWorkingPeople() {
        return (int) users.stream().filter((p) -> p.getAge() >= 18).filter(
                (p) -> (p.getSex().equals("woman") && p.getAge() < 55) || (p.getSex().equals("man") && p.getAge() < 60))
                .count();
    }

    /**
     * Удалить елемент из списка можно в одну строчку безо всякого труда
     * @param user
     * @return
     */

    public static List<User> removeUser(User user) {
        users.removeIf(i -> i.equals(user));
        return users;
    }

    public static void main(String[] args) {
        System.out.println(getUsersByAgeRangeAndSex(18, 30, "man"));
        System.out.println(getMensAverage());
        System.out.println(findCountOfWorkingPeople());
        System.out.println(removeUser(users.get(1)));

    }

}

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

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

Понравилась статья? Поделиться с друзьями:
Комментарии: 1
  1. Ллеонид

    static {
    List users = Arrays.asList(new User(1, «Ivan», 23, «man»),
    new User(2, «Olga», 45, «woman»),
    new User(3, «John», 12, «man»),
    new User(4, «Vitaliy», 25, «man») );
    }

    * вместо

    private static List users = new ArrayList();

    static {// создаем наших юзеров и заполняем ими коллекцию
    User user1 = new User(1, «Ivan», 23, «man»);
    User user2 = new User(2, «Olga», 45, «woman»);
    User user3 = new User(3, «John», 12, «man»);
    User user4 = new User(4, «Vitaliy», 25, «man»);
    users.add(user1);
    users.add(user2);
    users.add(user3);
    users.add(user4);
    }

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

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: