JavaRush/Java Blog/Random EN/Coffee break #108. 12 Common Uses of Java Streams, How to...

Coffee break #108. 12 Common Uses of Java Streams, How to Evaluate an Object's Memory Allocation in Java

Published in the Random EN group
members

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. Coffee break #108.  12 Common Uses of Java Streams, How to Evaluate an Object's Memory Allocation in Java - 1First, 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.

Now let's look at some use cases for 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.

Example: remove all odd numbers from the list.
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.

For example, let's convert each value to its square.
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.

As mentioned above, we can use the map() and collect() methods to transform a collection into another collection.

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.

Note that since the reduce() method returns a single value, it cannot be used to return a Collection. Example, we sum up all the values ​​in the list:
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 .

For example, let's group all products into product lists by their categories.
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 .

This is usually similar to a linear search. For example, we are looking for the first word in the list, the length of which exceeds 5 characters.
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 .

Generally, Collections.sort() is sufficient to sort a collection. We can use sorted() specifically if we want to run another operation. For example, let's sort a list of numbers in ascending order and then return the first k elements.
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 .

A split is similar to a group, except that it returns two collections—one for the elements that match the condition and one for the elements that don't match the condition. For example, let's divide students into those who passed the exam and those who failed it.
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 .

For example, let's count the number of words in the list whose length exceeds 5 characters.
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 .

There are special classes for creating streams of certain types - IntStream , LongStream , DoubleStream and Stream . These classes are useful when working with primitive numeric types. To convert an array into a stream, use Arrays.stream() . For example, let's create an array of numbers from 0 to 10.
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 .

For example, let's check products with a price higher than 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

  • Concatenates the elements of a collection into a string.

  • To join collection elements into a string, use the Collectors.joining() method .

For example, let's concatenate all the words in a list into one string.
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.

Thank you for reading. I hope you enjoyed this article. There are many more cases where threads can be used that are not covered in this topic. Feel free to add any common scenario that I missed.

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 . Coffee break #108.  12 Common Uses of Java Streams, How to Evaluate an Object's Memory Allocation in Java - 2The 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.
Comments
  • Popular
  • New
  • Old
You must be signed in to leave a comment
This page doesn't have any comments yet