12 распространенных способов использования Java Streams
Источник: Dev.to Java Streams API впервые появился в Java 8. Его цель — предоставить более компактный способ выполнения общих операций с коллекциями объектов. Также Java Streams API может использоваться для реализации сложных алгоритмов. В этой статье мы поговорим о распространенных случаях использования Java Streams. Для начала проясним некоторые основы:stream() — создает поток (Stream) из коллекции.
collect() — собирает поток в объект. Объект может быть коллекцией, примитивом или пользовательским классом.
Collectors — класс, который предоставляет (множество) статических методов для сбора потоков.
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)
Полезно, когда мы хотим преобразовать коллекцию в другую коллекцию.
Есть несколько способов добиться этого.
Пример 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() используется для применения функции к каждому элементу коллекции и возврата одного значения.
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().
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().
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().
long count = words.stream()
.filter(word -> word.length() > 5)
.count();
10. Диапазон (Range)
Создает диапазон значений.
Для создания диапазона значений используется метод range().
int[] numbers = IntStream.range(0, 10).toArray();
11. Соответствие (Matching)
Сопоставляет элементы коллекции с предикатом (условием).
Для сопоставления элементов коллекции с предикатом и возврата логического значения используются такие методы, как anyMatch(), allMatch(), и noneMatch().
// 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. Проблема с этим подходом заключается в том, что вам нужно подключиться к работающей 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.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ