JavaRush /Java блог /Random UA /Кава-брейк #108. 12 поширених способів використання Java ...

Кава-брейк #108. 12 поширених способів використання Java Streams, як оцінити виділення пам'яті об'єкта в Java

Стаття з групи Random UA

12 найпоширеніших способів використання Java Streams

Джерело: Dev.to Java Streams API вперше з'явився в Java 8. Його мета - надати компактніший спосіб виконання спільних операцій з колекціями об'єктів. Також Java Streams API може використовуватись для реалізації складних алгоритмів. У цій статті ми поговоримо про поширені випадки використання Java Streams. Кава-брейк #108.  12 поширених способів використання Java Streams, як оцінити виділення пам'яті об'єкта в Java - 1Для початку прояснимо деякі основи:
  • stream() – створює потік (Stream) з колекції.

  • collect() - Збирає потік в об'єкт. Об'єкт може бути колекцією, примітивом або класом користувача.

  • Collectors - клас, який надає (безліч) статичних методів для збору потоків.

А тепер давайте розглянемо деякі варіанти використання Streams:

1. Фільтрування (Filtering)

  • Використовується для видалення значень із колекції (Collection) на основі умови.

  • Для фільтрації елементів колекції з урахуванням умови використовується метод filter() . Зберігаються лише елементи, що збігаються.

Приклад: видаляємо всі непарні числа зі списку.
List<Integer> evenNumbers = originalList.stream()
        .filter(n -> n % 2 == 0)
        .collect(Collectors.toList());

2. Попередня обробка (Preprocessing)

  • Корисна, коли кожне значення в колекції потрібно змінити на місці.

  • Метод map() використовується для застосування функції кожного елемента колекції та повернення нової колекції обчислених значень.

Наприклад, перетворимо кожне значення на його квадрат.
List<Integer> squares = originalList.stream()
        .map(n -> n * n)
        .collect(Collectors.toList());

3. Перетворення (Conversion)

  • Корисно, коли ми хочемо перетворити колекцію на іншу колекцію.

  • Є кілька способів досягти цього.

Як згадувалося вище, ми можемо використовувати методи map() та collect() для перетворення колекції на іншу колекцію.

Приклад 1. Створити Map із Lists.

Перетворення списку рядків на карту рядків та довжини.
Map<String, Integer> wordLengths = words.stream()
        .collect(Collectors.toMap(
                word -> word,
                word -> word.length()));

Приклад 2. Перетворення списку (list) на набори (sets).

Це найпоширеніший варіант використання для видалення дублікатів. Крім того, якщо ми хочемо помістити елементи назад до списку, ми можемо двічі використовувати методи stream() та collect() . Наприклад, давайте перетворимо список рядків на список унікальних рядків:
// if we want to collect to a set
Set<String> uniqueWords = words.stream()
        .collect(Collectors.toSet());

// OR

// if we want to start and end as a list
List<String> uniqueWords = words.stream()
        .collect(Collectors.toSet()).stream().collect(Collectors.toList());

Приклад 3. Перетворення списку товарів на список їх назв. (Flattening - Вирівнювання)

List<String> productNames = products.stream()
        .map(product -> product.getName())
        .collect(Collectors.toList());

4. Скорочення (Reduction)

  • Скорочує Collection до значення.

  • Метод reduce() використовується для застосування функції до кожного елемента колекції та повернення одного значення.

Зауважте: оскільки метод reduce() повертає одне значення, його неможливо використовувати для повернення Collection. Наприклад, сумуємо всі значення у списку:
int sum = numbers.stream()
        .reduce(0, (a, b) -> a + b);

5. Угруповання (Grouping)

  • Групує елементи Collection за заданою умовою.

  • Для групи елементів Collection за умовою використовується метод Collectors.groupingBy() .

Наприклад, згрупуємо всі продукти до списків продуктів за їх категоріями.
Map<String, List<Product>> productsByCategory = products.stream()
        .collect(Collectors.groupingBy(product -> product.getCategory()));

6. Пошук (Finding)

  • Шукає перший або будь-який елемент Collection, який відповідає умові.

  • Для пошуку використовуються методи findFirst() та findAny() .

Зазвичай, це схоже на лінійний пошук. Наприклад, шукаємо перше слово у списку, довжина якого перевищує 5 символів.
Optional<String> firstLongWord = words.stream()
        .filter(word -> word.length() > 5)
        .findFirst();
// Note that findFirst() and findAny() methods return Optional<T> objects.

7. Сортування (Sorting)

  • Сортує елементи колекції.

  • Для сортування використовується метод sorted() .

Як правило, Collections.sort() достатньо для сортування колекції. Ми можемо використовувати sorted() спеціально, якщо хочемо запустити ще одну операцію. Наприклад, відсортуємо список чисел у порядку зростання, а потім повернемо перші k елементів.
List<Integer> topK = numbers.stream()
        .sorted()
        .limit(k)
        .collect(Collectors.toList());

8. Поділ (Partitioning)

  • Розділяє елементи Collection за заданою умовою.

  • Для розділення елементів використовується метод Collectors.partitioningBy() .

Поділ схожий на угруповання, за винятком того, що він повертає дві колекції — одну для елементів, що відповідають умові, та одну для елементів, що не відповідають умові. Наприклад, розділимо учнів на тих, хто склав іспит та тих, хто провалив його.
Map<Boolean, List<Student>> passingFailing = students
        .stream()
        .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

9. Підрахунок (Counting)

  • Підраховує кількість елементів, що відповідають умові.

  • Для підрахунку кількості елементів, що відповідають умові, використовується метод count() .

Наприклад, підрахуємо кількість слів у списку, довжина яких перевищує 5 символів.
long count = words.stream()
        .filter(word -> word.length() > 5)
        .count();

10. Діапазон (Range)

  • Створює діапазон значень.

  • Для створення діапазону значень використовують метод range() .

Існують спеціальні класи для створення потоків певних типів - IntStream , LongStream , DoubleStream і Stream . Ці класи корисні під час роботи з примітивними числовими типами. Для перетворення масиву на потік використовується Arrays.stream() . Наприклад, створюємо масив чисел від 0 до 10.
int[] numbers = IntStream.range(0, 10).toArray();

11. Відповідність (Matching)

  • Зіставляє елементи колекції з предикатом (умовою).

  • Для зіставлення елементів колекції з предикатом і повернення логічного значення використовуються такі методи, як anyMatch() , allMatch() і noneMatch() .

Наприклад, перевіримо товари із ціною вище 10.
// true when all elements match the predicate
boolean allMatch = products.stream()
        .allMatch(product -> product.getPrice() > 10);

// true when any element matches the predicate
boolean anyMatch = products.stream()
        .anyMatch(product -> product.getPrice() > 10);

// true when no elements match the predicate
boolean noneMatch = products.stream()
        .noneMatch(product -> product.getPrice() > 10);

12. Приєднання (Joining)

  • Об'єднує елементи колекції в рядок.

  • Для об'єднання елементів колекції в рядок використовується метод Collectors.joining() .

Наприклад, давайте об'єднаємо всі слова у списку в один рядок.
String joinedWords = words.stream()
        .collect(Collectors.joining(" "));
Ось і все для спільних сценаріїв. Є й інші менш поширені сценарії, які можна вивчити самостійно:
  • Паралельні потоки (Parallel Streams);
  • Статистика;
  • Користувальницькі колектори (Custom Collectors).

Переваги потоків

  • Більш компактний код — зменшує обсяг коду, який буде необхідний обробки колекції.

  • Менше проміжних змінних. Проміжні змінні можуть бути причиною помилок. Чим їх менше, тим простіше уникнути несподіваних помилок.

  • Інтуїтивно зрозумілий код. Деякі розробники не погодяться, що потоки більш інтуїтивно зрозумілі, ніж інші методи. Однак, як тільки ми звикнемо до них, вони стануть набагато інтуїтивнішими, ніж інші методи.

Дякую за читання. Сподіваюся, вам сподобалася ця стаття. Існує ще багато випадків, коли можна використовувати потоки, які не розглядаються у цій темі. Не соромтеся додавати будь-який розповсюджений сценарій, який я пропустив.

Як оцінити виділення пам'яті об'єкта в Java

Джерело: DZone У цій статті показано три найбільш відомі способи оцінки виділення пам'яті об'єкта в Java.

Оцінка пам'яті за допомогою Profiler

Найпростіший спосіб оцінити пам'ять деяких об'єктів - заглянути прямо в пам'ять JVM за допомогою профілювача, такого як Visual VM . Кава-брейк #108.  12 поширених способів використання Java Streams, як оцінити виділення пам'яті об'єкта в Java - 2Проблема з цим підходом полягає в тому, що вам потрібно підключитися до працюючої JVM, що може бути неможливо для виробничих середовищ з міркувань безпеки.

Оцінка пам'яті за допомогою інструментів

Інший спосіб оцінити виділену пам'ять для цього об'єкта - використовувати Instruments. Простіше кажучи, нам потрібно створити клас та скомпілювати його в JAR. Після створення JAR ми маємо виконати нашу JVM разом із цим JAR. Детально про цей спосіб можна дізнатися тут . Недоліком тут є необхідність додавання певного jar-файлу в JVM, що може бути неприйнятним для виробництва через проблеми з безпекою або пов'язані з цим проблеми.

Оцінка пам'яті за допомогою JOL Library

Як ще один варіант, ми можемо використовувати JOL Library . Це дуже потужна бібліотека, яка може надати детальну оцінку ваги об'єкта та пам'яті, виділеної екземпляром об'єкта. Щоб використати бібліотеку, нам потрібно додати залежність:
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.16</version>
</dependency>
Після цього ми можемо використовувати її так:
out.println(GraphLayout.parseInstance(myObject).totalSize() / 1024000d + " MB")

ObjectSizeCalculator з архіву Twitter

У відкритому репозиторії Twitter GitHub є клас інструменту ObjectSizeCalculator , який може оцінити виділену пам'ять для екземпляра об'єкта. Його використання не займає багато пам'яті чи часу. Процес оцінки займає секунди, навіть великих об'єктів. Використання цього класу досить просто:
ObjectSizeCalculator.getObjectSize(address)
Я рекомендую цей спосіб, але майте на увазі, що він підтримується лише Java Hotspot, OpenJDK та TwitterJDK.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ