java streams Archives - Piotr's TechBlog https://piotrminkowski.com/tag/java-streams/ Java, Spring, Kotlin, microservices, Kubernetes, containers Fri, 26 Apr 2024 07:57:00 +0000 en-US hourly 1 https://wordpress.org/?v=6.9.1 https://i0.wp.com/piotrminkowski.com/wp-content/uploads/2020/08/cropped-me-2-tr-x-1.png?fit=32%2C32&ssl=1 java streams Archives - Piotr's TechBlog https://piotrminkowski.com/tag/java-streams/ 32 32 181738725 Interesting Facts About Java Streams and Collections https://piotrminkowski.com/2024/04/25/interesting-facts-about-java-streams-and-collections/ https://piotrminkowski.com/2024/04/25/interesting-facts-about-java-streams-and-collections/#comments Thu, 25 Apr 2024 11:25:07 +0000 https://piotrminkowski.com/?p=15228 This article will show some interesting features of Java Streams and Collections you may not heard about. We will look at both the latest API enhancements as well as the older ones that have existed for years. That’s my private list of features I used recently or I just came across while reading articles about […]

The post Interesting Facts About Java Streams and Collections appeared first on Piotr's TechBlog.

]]>
This article will show some interesting features of Java Streams and Collections you may not heard about. We will look at both the latest API enhancements as well as the older ones that have existed for years. That’s my private list of features I used recently or I just came across while reading articles about Java. If you are interested in Java you can find some similar articles on my blog. In one of them, you can find a list of less-known but useful Java libraries.

Source Code

If you would like to try it by yourself, you may always take a look at my source code. In order to do that, you need to clone my GitHub repository. Once you clone the repository, you can switch to the jdk22 branch. Then you should just follow my instructions.

Mutable or Immutable

The approach to the collections immutability in Java can be annoying for some of you. How do you know if a Java Collection is mutable or immutable? Java does not provide dedicated interfaces and implementations for mutable or immutable collections like e.g. Kotlin. Of course, you can switch to the Eclipse Collections library, which provides a clear differentiation of types between readable, mutable, and immutable. However, if you are still with a standard Java Collections let’s analyze the situation by the example of the java.util.List interface. Some years ago Java 16 introduced a new Stream.toList() method to convert between streams and collections. Probably, you are using it quite often 🙂 It is worth mentioning that this method returns an unmodifiable List and allows nulls.

var l = Stream.of(null, "Green", "Yellow").toList();
assertEquals(3, l.size());
assertThrows(UnsupportedOperationException.class, () -> l.add("Red"));
assertThrows(UnsupportedOperationException.class, () -> l.set(0, "Red"));
Java

So, the Stream.toList() method is not just a replacement for the older approach based on the Collectors.toList(). On the other hand, an older Collectors.toList() method returns a modifiable List and also allows nulls.

var l = Stream.of(null, "Green", "Yellow").collect(Collectors.toList());
l.add("Red");
assertEquals(4, l.size());
Java

Finally, let’s see how to achieve the next possible option here. We need an unmodifiable List and does not allow nulls. In order to achieve it, we have to use the Collectors.toUnmodifiableList() method as shown below.

assertThrows(NullPointerException.class, () ->
        Stream.of(null, "Green", "Yellow")
                .collect(Collectors.toUnmodifiableList()));
Java

Grouping and Aggregations with Java Streams

Java Streams introduces several useful methods that allow us to group and aggregate collections using different criteria. We can find those methods in the java.util.stream.Collectors class. Let’s create the Employee record for testing purposes.

public record Employee(String firstName, 
                       String lastName, 
                       String position, 
                       int salary) {}
Java

Now, let’s assume we have a stream of employees and we want to calculate the sum of salary grouped by the position. In order to achieve this, we should combine two methods from the Collectors class: groupingBy and summingInt. If you would like to count the average salary by the position, you can just replace the summingInt method with the averagingInt method.

Stream<Employee> s1 = Stream.of(
    new Employee("AAA", "BBB", "developer", 10000),
    new Employee("AAB", "BBC", "architect", 15000),
    new Employee("AAC", "BBD", "developer", 13000),
    new Employee("AAD", "BBE", "tester", 7000),
    new Employee("AAE", "BBF", "tester", 9000)
);

var m = s1.collect(Collectors.groupingBy(Employee::position, 
   Collectors.summingInt(Employee::salary)));
assertEquals(3, m.size());
assertEquals(m.get("developer"), 23000);
assertEquals(m.get("architect"), 15000);
assertEquals(m.get("tester"), 16000);
Java

For more simple grouping we can the partitioningBy method. It always returns a map with two entries, one where the predicate is true and the second one where it is false. So, for the same stream as in the previous example, we can use partitioningBy to divide employees into those with salaries higher than 10000, and lower or equal to 10000.

var m = s1.collect(Collectors.partitioningBy(emp -> emp.salary() > 10000));
assertEquals(2, m.size());
assertEquals(m.get(true).size(), 2);
assertEquals(m.get(false).size(), 3);
Java

Let’s take a look at another example. This time, we will count the number of times each element occurs in the collection. Once again, we can use the groupingBy method, but this time in conjunction with the Collectors.counting() method.

var s = Stream.of(2, 3, 4, 2, 3, 5, 1, 3, 4, 4)
   .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
assertEquals(5, m.size());
assertEquals(m.get(4), 3);
Java

The Map.merge() Method

In the previous examples, we use methods provided by Java Streams to perform grouping and aggregations. Now, the question is if we can do the same thing with the standard Java collections without converting them to streams. The answer is yes – we can easily do it with the Map.merge() method. It is probably the most versatile operation among all the Java key-value methods. The Map.merge() method either puts a new value under the given key (if absent) or updates the existing key with a given value. Let’s rewrite the previous examples, to switch from Java streams to collections. Here’s the implementation for counting the number of times each element occurs in the collection.

var map = new HashMap<Integer, Integer>();
var nums = List.of(2, 3, 4, 2, 3, 5, 1, 3, 4, 4);
nums.forEach(num -> map.merge(num, 1, Integer::sum));
assertEquals(5, map.size());
assertEquals(map.get(4), 3);
Java

Then, we can implement the operation of calculating the sum of salary grouped by the position. So, we are grouping by the emp.position() and calculating the total salary by summing the previous value with the value taken from the current Employee in the list. The results are the same as in the examples from the previous section.

var s1 = List.of(
   new Employee("AAA", "BBB", "developer", 10000),
   new Employee("AAB", "BBC", "architect", 15000),
   new Employee("AAC", "BBD", "developer", 13000),
   new Employee("AAD", "BBE", "tester", 7000),
   new Employee("AAE", "BBF", "tester", 9000)
);
var map = new HashMap<String, Integer>();
s1.forEach(emp -> map.merge(emp.position(), emp.salary(), Integer::sum));
assertEquals(3, map.size());
assertEquals(map.get("developer"), 23000);
Java

Use EnumSet for Java Enum

If you are storing enums inside Java collections you should use EnumSet instead of e.g. more popular HashSet. The EnumSet and EnumMap collections are specialized versions of Set and Map that are built for enums. Those abstracts guarantee less memory consumption and much better performance. They are also providing some methods dedicated to simplifying integration with Java Enum. In order to compare processing time between EnumSet and a standard Set we can prepare a simple test. In this test, I’m creating a subset of Java Enum inside the EnumSet and then checking out if all the values exist in the target EnumSet.

var x = EnumSet.of(
    EmployeePosition.SRE,
    EmployeePosition.ARCHITECT,
    EmployeePosition.DEVELOPER);
long beg = System.nanoTime();
for (int i = 0; i < 100_000_000; i++) {
   var es = EnumSet.allOf(EmployeePosition.class);
   es.containsAll(x);
}
long end = System.nanoTime();
System.out.println(x.getClass() + ": " + (end - beg)/1e9);
Java

Here’s a similar test without EnumSet:

var x = Set.of(
    EmployeePosition.SRE,
    EmployeePosition.ARCHITECT,
    EmployeePosition.DEVELOPER);
long beg = System.nanoTime();
for (int i = 0; i < 100_000_000; i++) {
   var hs = Set.of(EmployeePosition.values());
   hs.containsAll(x);
}
long end = System.nanoTime();
System.out.println(x.getClass() + ": " + (end - beg)/1e9);
Java

The difference in time consumption before both two variants visible above is pretty significant.

class java.util.ImmutableCollections$SetN: 8.577672411
class java.util.RegularEnumSet: 0.184956851
ShellSession

Java 22 Stream Gatherers

The latest JDK 22 release introduces a new addition to Java streams called gatherers. Gatherers enrich the Java Stream API with capabilities for custom intermediate operations. Thanks to that we can transform data streams in ways that were previously complex or not directly supported by the existing API. First of all, this is a preview feature, so we need to explicitly enable it in the compiler configuration. Here’s the modification in the Maven pom.xml:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.13.0</version>
  <configuration>
    <release>22</release>
    <compilerArgs>--enable-preview</compilerArgs>
  </configuration>
</plugin>
XML

The aim of that article is not to provide you with a detailed explanation of Stream Gatherers. So, if you are looking for an intro it’s worth reading the following article. However, just to give you an example of how we can leverage gatherers I will provide a simple implementation of a circuit breaker with the slidingWindow() method. In order to show you, what exactly happens inside I’m logging the intermediate elements with the peek() method. Our implementation will open the circuit breaker if there are more than 10 errors in the specific period.

var errors = Stream.of(2, 0, 1, 3, 4, 2, 3, 0, 3, 1, 0, 0, 1)
                .gather(Gatherers.windowSliding(4))
                .peek(a -> System.out.println(a))
                .map(x -> x.stream().collect(summing()) > 10)
                .toList();
System.out.println(errors);
Java

The size of our sliding window is 4. Therefore, each time the slidingWindow() method creates a list with 4 subsequent elements. For each intermediate list, I’m summing the values and checking if the total number of errors is greater than 10. Here’s the output. As you see only the circuit breaker is opened for the [3, 4, 2, 3] fragment of the source stream.

[2, 0, 1, 3]
[0, 1, 3, 4]
[1, 3, 4, 2]
[3, 4, 2, 3]
[4, 2, 3, 0]
[2, 3, 0, 3]
[3, 0, 3, 1]
[0, 3, 1, 0]
[3, 1, 0, 0]
[1, 0, 0, 1]
[false, false, false, true, false, false, false, false, false, false]
Java

Use the Stream.reduce() Method

Finally, the last feature in our article. I believe that the Stream.reduce() method is not a very well-known and widely used stream method. However, it is very interesting. For example, we can use it to sum all the numbers in the Java List. The first parameter of the reduce method is an initial value, while the second is an accumulator algorithm.

var listOfNumbers = List.of(1, 2, 3, 4, 5);
var sum = listOfNumbers.stream().reduce(0, Integer::sum);
assertEquals(15, sum);
Java

Final Thoughts

Of course, it is just a small list of interesting facts about Java streams and collections. If you have some other favorite features you can put them in the article comments.

The post Interesting Facts About Java Streams and Collections appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2024/04/25/interesting-facts-about-java-streams-and-collections/feed/ 3 15228
Express JPA Queries as Java Streams https://piotrminkowski.com/2021/07/13/express-jpa-queries-as-java-streams/ https://piotrminkowski.com/2021/07/13/express-jpa-queries-as-java-streams/#comments Tue, 13 Jul 2021 11:00:11 +0000 https://piotrminkowski.com/?p=9949 In this article, you will learn how to use the JPAstreamer library to express your JPA queries with Java streams. I will also show you how to integrate this library with Spring Boot and Spring Data. The idea around it is very simple but at the same time brilliant. The library creates a SQL query […]

The post Express JPA Queries as Java Streams appeared first on Piotr's TechBlog.

]]>
In this article, you will learn how to use the JPAstreamer library to express your JPA queries with Java streams. I will also show you how to integrate this library with Spring Boot and Spring Data. The idea around it is very simple but at the same time brilliant. The library creates a SQL query based on your Java stream. That’s all. I have already mentioned this library on my Twitter account.

jpa-java-streams-twitter

Before we start, let’s take a look at the following picture. It should explain the concept in a simple way. That’s pretty intuitive, right?

jpa-java-streams-table

Source Code

If you would like to try it by yourself, you may always take a look at my source code. In order to do that you need to clone my GitHub repository. Then you should just follow my instructions. Let’s begin.

Dependencies and configuration

As an example, we have a simple Spring Boot application that runs an embedded H2 database and exposes data through a REST API. It also uses Spring Data JPA to interact with the database. But with the JPAstreamer library, this is completely transparent for us. So, in the first step, we need to include the following two dependencies. The first of them adds JPAstreamer while the second integrate it with Spring Boot.

<dependency>
  <groupId>com.speedment.jpastreamer</groupId>
  <artifactId>jpastreamer-core</artifactId>
  <version>1.0.1</version>
</dependency>
<dependency>
  <groupId>com.speedment.jpastreamer.integration.spring</groupId>
  <artifactId>spring-boot-jpastreamer-autoconfigure</artifactId>
  <version>1.0.1</version>
</dependency>

Then, we need to add Spring Boot Web and JPA starters, H2 database, and optionally Lombok.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.20</version>
</dependency>

I’m using Java 15 for compilation. Because I use Java records for DTO I need to enable preview features. Here’s the plugin responsible for it.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.8.1</version>
  <configuration>
    <release>15</release>
    <compilerArgs>
        --enable-preview
    </compilerArgs>
    <source>15</source>
    <target>15</target>
  </configuration>
</plugin>

The JPAstreamer library generates source code based on your entity model. Then we may use it, for example, to perform filtering or sorting. But we will talk about it in the next part of the article. For now, let’s configure the build process with build-helper-maven-plugin. It generates the source code in the target/generated-sources/annotations directory. If you use IntelliJ it is automatically included as a source folder in your project.

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>build-helper-maven-plugin</artifactId>
  <version>3.2.0</version>
  <executions>
    <execution>
      <phase>generate-sources</phase>
      <goals>
        <goal>add-source</goal>
      </goals>
      <configuration>
        <sources>
          <source>${project.build.directory}/generated-sources/annotations</source>
        </sources>
      </configuration>
    </execution>
  </executions>
</plugin>

Here is a source code generated by JPAstreamer for our entity model.

Entity model for JPA

Let’s take a look at our example entities. Here’s the Employee class. Each employee is assigned to the department and organization.

@Entity
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Employee {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;
   private String name;
   private String position;
   private int salary;
   @ManyToOne(fetch = FetchType.LAZY)
   private Department department;
   @ManyToOne(fetch = FetchType.LAZY)
   private Organization organization;
}

Here’s the Department entity.

@Entity
@NoArgsConstructor
@Getter
@Setter
public class Department {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;
   private String name;
   @OneToMany(mappedBy = "department")
   private Set<Employee> employees;
   @ManyToOne(fetch = FetchType.LAZY)
   private Organization organization;
}

Here’s the Organization entity.

@Entity
@NoArgsConstructor
@Getter
@Setter
public class Organization {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Integer id;
   private String name;
   @OneToMany(mappedBy = "organization")
   private Set<Department> departments;
   @OneToMany(mappedBy = "organization")
   private Set<Employee> employees;
}

We will also use Java records to create DTOs. Here’s a simple DTO for the Employee entity.

public record EmployeeDTO(
   Integer id,
   String name,
   String position,
   int salary
) {
   public EmployeeDTO(Employee emp) {
      this(emp.getId(), emp.getName(), emp.getPosition(), emp.getSalary());
   }
}

We also have a DTO record to express relationship fields.

public record EmployeeWithDetailsDTO(
   Integer id,
   String name,
   String position,
   int salary,
   String organizationName,
   String departmentName
) {
   public EmployeeWithDetailsDTO(Employee emp) {
      this(emp.getId(), emp.getName(), emp.getPosition(), emp.getSalary(),
            emp.getOrganization().getName(),
            emp.getDepartment().getName());
   }
}

Express JPA queries as Java streams

Let’s begin with a simple example. We would like to get all the departments, sort it ascending based on the name field, and then convert it to DTO. We just need to get an instance of JPAstreamer object and invoke a stream() method. Then you do everything else as you would act with standard Java streams.

@GetMapping
public List<DepartmentDTO> findAll() {
   return streamer.stream(Department.class)
        .sorted(Department$.name)
        .map(DepartmentDTO::new)
        .collect(Collectors.toList());
}

Now, we can call the endpoint after starting our Spring Boot application.

$ curl http://localhost:8080/departments
[{"id":4,"name":"aaa"},{"id":3,"name":"bbb"},{"id":2,"name":"ccc"},{"id":1,"name":"ddd"}]

Let’s take a look at something a little bit more advanced. We are going to find employees with salaries greater than an input value, sort them by salaries, and of course map to DTO.

@GetMapping("/greater-than/{salary}")
public List<EmployeeDTO> findBySalaryGreaterThan(@PathVariable("salary") int salary) {
   return streamer.stream(Employee.class)
         .filter(Employee$.salary.greaterThan(salary))
         .sorted(Employee$.salary)
         .map(EmployeeDTO::new)
         .collect(Collectors.toList());
}

Then, we call another endpoint once again.

$ curl http://localhost:8080/employees/greater-than/25000    
[{"id":5,"name":"Test5","position":"Architect","salary":30000},{"id":7,"name":"Test7","position":"Manager","salary":30000},{"id":9,"name":"Test9","position":"Developer","salary":30000}]

We can also perform JPA pagination operations by using skip and limit Java streams methods.

@GetMapping("/offset/{offset}/limit/{limit}")
public List<EmployeeDTO> findAllWithPagination(
      @PathVariable("offset") int offset, 
      @PathVariable("limit") int limit) {
   return streamer.stream(Employee.class)
         .skip(offset)
         .limit(limit)
         .map(EmployeeDTO::new)
         .collect(Collectors.toList());
}

What is important all such operations are performed on the database side. Here’s the SQL query generated for the implementation visible above.

What about relationships between entities? Of course relationships between tables are handled via the JPA provider. In order to perform the JOIN operation with JPAstreamer, we just need to specify the joining stream. By default, it is LEFT JOIN, but we can customize it when calling the joining() method. In the following fragment of code, we join Department and Organization, which are in @ManyToOne relationship with the Employee entity.

@GetMapping("/{id}")
public EmployeeWithDetailsDTO findById(@PathVariable("id") Integer id) {
   return streamer.stream(of(Employee.class)
           .joining(Employee$.department)
           .joining(Employee$.organization))
        .filter(Employee$.id.equal(id))
        .map(EmployeeWithDetailsDTO::new)
        .findFirst()
        .orElseThrow();
}

Of course, we can call many other Java stream methods. In the following fragment of code, we count the number of employees assigned to the particular department.

@GetMapping("/{id}/count-employees")
public long getNumberOfEmployees(@PathVariable("id") Integer id) {
   return streamer.stream(Department.class)
         .filter(Department$.id.equal(id))
         .map(Department::getEmployees)
         .mapToLong(Set::size)
         .sum();
}

And the last example today. We get all the employees assigned to a particular department and map each of them to EmployeeDTO.

@GetMapping("/{id}/employees")
public List<EmployeeDTO> getEmployees(@PathVariable("id") Integer id) {
   return streamer.stream(Department.class)
         .filter(Department$.id.equal(id))
         .map(Department::getEmployees)
         .flatMap(Set::stream)
         .map(EmployeeDTO::new)
         .collect(Collectors.toList());
}

Integration with Spring Boot

We can easily integrate JPAstreamer with Spring Boot and Spring Data JPA. In fact, you don’t have anything more than just include a dependency responsible for integration with Spring. It provides auto-configuration also for Spring Data JPA. Therefore, we just need to inject the JPAStreamer bean into the target service or controller.

@RestController
@RequestMapping("/employees")
public class EmployeeController {

   private final JPAStreamer streamer;

   public EmployeeController(JPAStreamer streamer) {
      this.streamer = streamer;
   }

   @GetMapping("/greater-than/{salary}")
   public List<EmployeeDTO> findBySalaryGreaterThan(
      @PathVariable("salary") int salary) {
   return streamer.stream(Employee.class)
        .filter(Employee$.salary.greaterThan(salary))
        .sorted(Employee$.salary)
        .map(EmployeeDTO::new)
        .collect(Collectors.toList());
   }

   // ...

}

Final Thoughts

The concept around the JPAstreamer library seems to be very interesting. I really like it. The only disadvantage I found is that it sends certain data back to Speedment’s servers for Google Analytics. If you wish to disable this feature, you need to contact their team. To be honest, I’m concerned a little. But it doesn’t change my point of view, that JPAstreamer is a very useful library. If you are interested in topics related to Java streams and collections you may read my article Using Eclipse Collections.

The post Express JPA Queries as Java Streams appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2021/07/13/express-jpa-queries-as-java-streams/feed/ 21 9949
Using Eclipse Collections https://piotrminkowski.com/2021/06/22/using-eclipse-collections/ https://piotrminkowski.com/2021/06/22/using-eclipse-collections/#respond Tue, 22 Jun 2021 15:47:50 +0000 https://piotrminkowski.com/?p=9852 In this article, you will learn about some useful features provided by Eclipse Collections. Do you feel that Java Streams API is sometimes not enough? I think it’s worth taking a look at Eclipse Collections. Let’s discuss why. Mutable or Immutable If you like the Kotlin collections API this concept appeals to you. You can […]

The post Using Eclipse Collections appeared first on Piotr's TechBlog.

]]>
In this article, you will learn about some useful features provided by Eclipse Collections. Do you feel that Java Streams API is sometimes not enough? I think it’s worth taking a look at Eclipse Collections. Let’s discuss why.

Mutable or Immutable

If you like the Kotlin collections API this concept appeals to you. You can create mutable and immutable collections. Only mutable collections provide methods for adding new objects.

Person p1 = new Person("Test1", 20);
Person p2 = new Person("Test2", 18);
Person p3 = new Person("Test3", 24);
MutableList<Person> persons = Lists.mutable.with(p1);
persons.add(p2);
persons.set(1, p3);

The same methods are not available for e.g. ImmutableList and cause compilation error.

Person p1 = new Person("Test1", 20);
Person p2 = new Person("Test2", 18);
Person p3 = new Person("Test3", 24);
ImmutableList<Person> persons = Lists.immutable.with(p1);
persons.add(p2); // ERROR!
persons.set(1, p3); // ERROR!

Lazy collections

Another interesting feature of Eclipse Collections, called lazy collections, allows you to defer execution until a terminal operation is invoked. You can enable the lazy API by calling the asLazy method. Let’s see how it works. Firstly, we have the following class. I added a single log message inside the getAge method.

public class Person {

    private final String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        System.out.println("Age: " + this.age);
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Then let’s create a lazy collection. In the first step we increase the age of each person by 2. Then we filter the persons with age higher than 20. Finally, we get only a first element from a result stream.

Person p1 = new Person("Test1", 20);
Person p2 = new Person("Test2", 18);
Person p3 = new Person("Test3", 24);
Person p4 = new Person("Test4", 30);
Person p5 = new Person("Test5", 35);
MutableList<Person> persons = Lists.mutable.with(p1, p2, p3, p4, p5);
persons
   .asLazy()
   .tap(p -> p.setAge(p.getAge() + 2))
   .select(p -> p.getAge() > 20)
   .getFirst();

What’s the output of the code visible above? Because we get only a first element from a list, it calls the getAge method only for a first filtered object.

Age: 20
Age: 22

Now, let’s do the same operation for non-lazy collection.

Person p1 = new Person("Test1", 20);
Person p2 = new Person("Test2", 18);
Person p3 = new Person("Test3", 24);
Person p4 = new Person("Test4", 30);
Person p5 = new Person("Test5", 35);
MutableList<Person> persons = Lists.mutable.with(p1, p2, p3, p4, p5);
persons
   .tap(p -> p.setAge(p.getAge() + 2))
   .select(p -> p.getAge() > 20)
   .getFirst();

Here’s the result. We call the getAge method twice for every single object in the list although we finally take only a first object.

Age: 22
Age: 18
Age: 24
Age: 30
Age: 35
Age: 24
Age: 20
Age: 26
Age: 32
Age: 37

Collect elements

With Eclipse Collections we can easily collect and filter elements using a single method. Let’s consider the following operation with Java Streams. We have the input list with organizations. Then we want to filter only an organization with Test1 name, get all employees from this organization and then convert it to the list of employees.

Employee e1 = new Employee(1, "Test1", "Developer");
Employee e2 = new Employee(2, "Test2", "Developer");
Employee e3 = new Employee(3, "Test3", "Developer");
Employee e4 = new Employee(4, "Test4", "Developer");
Organization o1 = new Organization("Test1", List.of(e1, e2));
Organization o2 = new Organization("Test2", List.of(e3, e4));
List<Organization> organizations = List.of(o1, o2);
List<Employee> employees = organizations
   .stream()
   .filter(o -> o.name().equals("Test1"))
   .map(Organization::employees)
   .flatMap(List::stream)
   .collect(Collectors.toList());

With Eclipse Collections you can do the same thing using the following two methods.

MutableList<Employee> employees = organizations
   .select(o -> o.name().equals("Test1"))
   .flatCollect(Organization::employees);

Similarly, if you would like to collect employees without flatting with Java Streams you should do that as shown below.

List<List<Employee>> employees2 = organizations
   .stream()
   .filter(o -> o.name().equals("Test1"))
   .map(Organization::employees)
   .collect(Collectors.toList());

On the other hand, with Eclipse Collections you can do that using a single method collectIf.

MutableList<List<Employee>> employees2 = organizations
   .collectIf(o -> o.name().equals("Test1"), Organization::employees);

Arithmetic operations

With Eclipse Collections we can easily do some arithmetic operations like counting or searching for mix or max element. In the following fragment of code, we want to count the number of employees with a position Developer.

Employee e1 = new Employee(1, "Test1", "Developer");
Employee e2 = new Employee(2, "Test2", "Architect");
Employee e3 = new Employee(3, "Test3", "Developer");
Employee e4 = new Employee(4, "Test4", "Tester");
List<Employee> employees = List.of(e1, e2, e3, e4);
long c = employees
   .stream()
   .filter(emp -> emp.position().equals("Developer"))
   .count();

With Eclipse Collections we may use a single method once again.

MutableList<Employee> employees = Lists.mutable.of(e1, e2, e3, e4);
int c = employees.count(emp -> emp.position().equals("Developer"));

Similarly, with Java Streams looking for a minimal element in the list is a little bit complicated. We want to find employee with the lowest id.

Employee r1 = employees
   .stream()
   .min(Comparator.comparing(Employee::id))
   .orElseThrow(NoSuchElementException::new);

As you probably guess, we may do the same operation by calling a single method on Eclipse collection.

Employee r1 = employees.minBy(Employee::id);

Final Thoughts

I described only some selected methods provided by Eclipse Collections. For a detailed description of the whole API, you may refer to their documentation. Another reason that may convince you to switch to Eclipse Collections is resource usage. Following the documentation, it provides a memory-efficient implementation of sets, maps, and other primitive collections. Also if you are interested in other articles about Java you may read New Developer Friendly Features After Java 8.

The post Using Eclipse Collections appeared first on Piotr's TechBlog.

]]>
https://piotrminkowski.com/2021/06/22/using-eclipse-collections/feed/ 0 9852