12 Common Ways to Use Java Streams
Source: Dev.to The Java Streams API first appeared in Java 8. Its purpose is to provide a more compact way to perform common operations on collections of objects. Also, the Java Streams API can be used to implement complex algorithms. In this article, we will talk about common use cases of Java Streams. First, let's clear up some basics:-
stream() - creates a stream from the collection.
-
collect() - collects a stream into an object. An object can be a collection, a primitive, or a custom class.
-
Collectors is a class that provides (many) static methods for collecting streams.
1. Filtering
-
Used to remove values from a Collection based on a condition.
-
To filter collection elements based on a condition, use the filter() method . Only matching elements are saved.
List<Integer> evenNumbers = originalList.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
2. Preprocessing
-
Useful when every value in a collection needs to be changed in place.
-
The map() method is used to apply a function to each element of the collection and return a new collection of calculated values.
List<Integer> squares = originalList.stream()
.map(n -> n * n)
.collect(Collectors.toList());
3. Conversion
-
Useful when we want to transform a collection into another collection.
-
There are several ways to achieve this.
Example 1: Create a Map from Lists.
Convert a list of strings to a map of strings and lengths.Map<String, Integer> wordLengths = words.stream()
.collect(Collectors.toMap(
word -> word,
word -> word.length()));
Example 2. Converting a list into sets.
This is a common use case for removing duplicates. Additionally, if we want to put elements back into the list, we can use the stream() and collect() methods twice . For example, let's convert a list of strings to a list of unique strings:// 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());
Example 3. Converting a list of products into a list of their names. (Flattening - Alignment)
List<String> productNames = products.stream()
.map(product -> product.getName())
.collect(Collectors.toList());
4. Reduction
-
Reduces Collection to a single value.
-
The reduce() method is used to apply a function to each element of the collection and return a single value.
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
5. Grouping
-
Groups the elements of a Collection based on a given condition.
-
To group Collection elements by condition, use the Collectors.groupingBy() method .
Map<String, List<Product>> productsByCategory = products.stream()
.collect(Collectors.groupingBy(product -> product.getCategory()));
6. Finding
-
Searches for the first or any Collection element that matches a condition.
-
The findFirst() and findAny() methods are used for searching .
Optional<String> firstLongWord = words.stream()
.filter(word -> word.length() > 5)
.findFirst();
// Note that findFirst() and findAny() methods return Optional<T> objects.
7. Sorting
-
Sorts the elements of Collections.
-
The sorted() method is used for sorting .
List<Integer> topK = numbers.stream()
.sorted()
.limit(k)
.collect(Collectors.toList());
8. Partitioning
-
Separates the elements of a Collection based on a given condition.
-
The Collectors.partitioningBy() method is used to separate elements .
Map<Boolean, List<Student>> passingFailing = students
.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
9. Counting
-
Counts the number of elements that match a condition.
-
The count() method is used to count the number of elements that match a condition .
long count = words.stream()
.filter(word -> word.length() > 5)
.count();
10. Range
-
Creates a range of values.
-
To create a range of values, use the range() method .
int[] numbers = IntStream.range(0, 10).toArray();
11. Matching
-
Matches the elements of a collection with a predicate (condition).
-
Methods such as anyMatch() , allMatch() , and noneMatch() are used to match collection elements with a predicate and return a boolean value .
// 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
-
Concatenates the elements of a collection into a string.
-
To join collection elements into a string, use the Collectors.joining() method .
String joinedWords = words.stream()
.collect(Collectors.joining(" "));
That's it for general scenarios. There are other less common scenarios that you can explore on your own:
- Parallel Streams;
- Statistics;
- Custom Collectors.
Benefits of Threads
-
More compact code—reduces the amount of code required to process the collection.
-
Fewer intermediate variables. Intervening variables can cause errors to occur. The fewer there are, the easier it is to avoid unexpected mistakes.
-
Intuitive code. Some developers will disagree that threads are more intuitive than other methods. However, once we get used to them, they become much more intuitive than other methods.
How to evaluate an object's memory allocation in Java
Source: DZone This article shows the three most well-known ways to evaluate an object's memory allocation in Java.Memory assessment using Profiler
The easiest way to estimate the memory of some objects is to look directly into the JVM memory using a profiler such as Visual VM . The problem with this approach is that you need to connect to a running JVM, which may not be possible for production environments due to security reasons.Memory assessment using Instruments
Another way to estimate the allocated memory for a given object is to use Instruments. In simple terms, we need to create a class and compile it into a JAR. After creating the JAR, we must execute our JVM along with that JAR. You can find out more about this method here . The downside here is the need to add a specific jar file to the JVM, which may not be acceptable for production due to security or related issues.Memory assessment using the JOL Library
As another option, we can use JOL Library . This is a very powerful library that can provide a detailed estimate of the weight of an object and the memory allocated by an object instance. To use the library, we need to add a dependency:<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
After that we can use it like this:
out.println(GraphLayout.parseInstance(myObject).totalSize() / 1024000d + " MB")
ObjectSizeCalculator from Twitter archive
Twitter's public GitHub repository has a tool class called ObjectSizeCalculator that can estimate the allocated memory for a given object instance. It doesn't take much memory or time to use. The evaluation process takes seconds, even for large objects. Using this class is quite simple:ObjectSizeCalculator.getObjectSize(address)
I recommend this method, but keep in mind that it is only supported by Java Hotspot, OpenJDK and TwitterJDK.
GO TO FULL VERSION