JavaRush /Java блог /Random /Кофе-брейк #108. 12 распространенных способов использован...

Кофе-брейк #108. 12 распространенных способов использования Java Streams, как оценить выделение памяти объекта в Java

Статья из группы Random

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)

  • Сортирует элементы Collections.

  • Для сортировки используется метод 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

Другой способ оценить выделенную память для данного объекта — использовать 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.
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ