JavaRush /Java блог /Random UA /Позбавляємося циклів у Java 8
KapChook
19 рівень
Volga

Позбавляємося циклів у Java 8

Стаття з групи Random UA
Функціональний стиль, представлений Java 8, - велика добавка до мови. Тепер Java – це не чисте ООП, тепер це гібрид ООП та функціонального програмування. Це змінює правила гри і нам потрібно змінити свої ООП-мозки, щоб увібрати ці зміни. Позбавляємося циклів в Java 8 - 1Але чому ми маємо приймати ці зміни? Чому маємо витрачати час у спробах ужитися з функціональним стилем, коли ми можемо вирішити проблему на чистому ОВП?
  • Функціональний стиль, представлений у Java 8, допомагає нам зменшити прірву між бізнес-логікою та кодом. Він дозволяє нам розповідати історію у природному потоці на вищому рівні. Замість того, щоб говорити, як ви хочете це зробити, ви можете сказати, що ви хочете зробити.

  • Код стає більш чистим та коротким.

  • Функції високого порядку дозволяють нам:

    • Надсилати функції до інших функцій
    • Створювати функції усередині інших функцій
    • Повертати функції з інших функцій

    Це велика перемога для Java, де нам потрібно відправляти, створювати і повертати об'єкти. Ми зможемо писати код, який буде більш надійним, зосередженим і легшим для повторного використання.

  • Завдяки лямбдам ми можемо робити ліниві обчислення. Коли лямбда-вираз відправляється як аргумент методу, компілятор обчислить його, коли він викликається методом. Це відрізняється від звичайних аргументів методів, які обчислюються одразу.

  • Лямбди роблять написання unit-тестів веселим. Вони дозволяють нам створювати легковажні тести, які чисті, малі за розміром та швидкі в написанні. Ми можемо корчувати код, що тестується, використовуючи лямбди. Це дозволяє нам тестувати, як усі види сценаріїв вплинуть на код.

  • Нові візерунки для вивчення.

  • І багато іншого!

Але вистачить води, у цій статті ми поглянемо альтернативні рішення для традиційних циклів. Звичайно цикли гнучкі, але це не дається без своєї ціни. break, continue, returnрізко змінюють поведінку циклу, змушуючи нас розуміти як, чого код намагається досягти, а й розуміти, як працює цикл. Зараз ми поглянемо, як ми можемо перетворити цикли на більш короткий і читальний код.

Та почнеться кодинг!

Ми працюватимемо зі статтями. Стаття має назву, автор і кілька тегів.
private class Article {

    private final String title;
    private final String author;
    private final List<String> tags;

    private Article(String title, String author, List<String> tags) {
        this.title = title;
        this.author = author;
        this.tags = tags;
    }

    public String getTitle() {
        return title;
    }

    public String getAuthor() {
        return author;
    }

    public List<String> getTags() {
        return tags;
    }
}
Кожен приклад буде містити традиційне рішення з використанням циклів і рішення, що використовує нові фішки Java 8. У першому прикладі хочемо знайти в колекції першу статтю з тегом "Java". Погляньмо на рішення з використанням циклу.
public Article getFirstJavaArticle() {

    for (Article article : articles) {
        if (article.getTags().contains("Java")) {
            return article;
        }
    }
    return null;
}
Тепер давайте вирішимо проблему, користуючись операціями із Stream API.
public Optional<Article> getFirstJavaArticle() {
    return articles.stream()
        .filter(article -> article.getTags().contains("Java"))
        .findFirst();
    }
Досить таки круто, чи не так? Спочатку ми використовуємо операцію filterдля знаходження всіх статей з тегом "Java", потім використовуємо findFirst()для отримання першого входження. Так як потоки (streams) ліниві і фільтр повертає потік, цей підхід оброблятиме елементи тільки поки не знайде перший збіг. Тепер давайте отримаємо всі статті з тегом Java, замість тільки першої. Спершу рішення за допомогою циклів.
public List<Article> getAllJavaArticles() {

    List<Article> result = new ArrayList<>();

    for (Article article : articles) {
        if (article.getTags().contains("Java")) {
            result.add(article);
        }
    }
    return result;
}
Рішення із використанням потокових операцій.
public List<Article> getAllJavaArticles() {
    return articles.stream()
        .filter(article -> article.getTags().contains("Java"))
        .collect(Collectors.toList());
    }
У цьому прикладі ми використовували операцію collectдля скорочення результуючого потоку замість оголошення колекції та явного додавання статей, які підходять. Поки що все йде добре. Час для прикладів, які змусять Stream API справді блищати. Давайте згрупуємо всі статті з автора. Як завжди, починаємо з рішення за допомогою циклів:
public Map<String, List<Article>> groupByAuthor() {

    Map<String, List<Article>> result = new HashMap<>();

    for (Article article : articles) {
        if (result.containsKey(article.getAuthor())) {
            result.get(article.getAuthor()).add(article);
        } else {
            ArrayList<Article> articles = new ArrayList<>();
            articles.add(article);
            result.put(article.getAuthor(), articles);
        }
    }
    return result;
}
Чи зможемо знайти чисте вирішення цієї проблеми, використовуючи потокові операції?
public Map<String, List<Article>> groupByAuthor() {
    return articles.stream()
        .collect(Collectors.groupingBy(Article::getAuthor));
}
Чудово! Використовуючи операцію groupingByта посилання на метод getAuthor(), ми отримуємо чистий та читабельний код. Тепер знайдемо інші теги, що використовуються в колекції. Почнемо з циклового прикладу:
public Set<String> getDistinctTags() {

    Set<String> result = new HashSet<>();

    for (Article article : articles) {
        result.addAll(article.getTags());
    }
    return result;
}
Окей, погляньмо, як ми можемо вирішити це за допомогою потокових операцій:
public Set<String> getDistinctTags() {
    return articles.stream()
        .flatMap(article -> article.getTags().stream())
        .collect(Collectors.toSet());
}
Круто! flatmapдопомагає нам згладити список тегів в один результуючий потік, а потім ми використовуємо collectдля створення сету, що повертається.

Нескінченні можливості

Це були 4 приклади, як можна замінити цикли на більш читабельний код. Обов'язково ознайомтеся з Stream API, так як ця стаття лише пошкріб її поверхню. Освоєння нового функціонального стилю Java буде випробуванням для ООП-розробників, але це випробування, яке має бути добре прийняте. Я навіть піду далі і скажу, що варто вивчити чисту функціональну мову програмування. Таким чином ви зможете повністю зрозуміти можливості та міць, які він надає. Я думаю, це допоможе вам зрозуміти функціональне програмування на іншому рівні. Так що освоюйте функціональне програмування, поряд зі старим добрим ОВП, та використовуйте їх обох для написання ще більшого коду! Вільний мікс із перекладів двох статей — Why you should embrace functional programming in Java 8 таSwerving Away from Loops in Java 8
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ