Pages

Wednesday, September 8, 2021

Java 8 Stream.collect() examples

Java 8 Stream interface provides a method called collect() which helps to perform various kind of manipulations of the elements in a Stream. These include

  • Combining all the elements of a Stream into a different data structures like List and Set.
  • Reducing and summarizing operations like count, max, min.
  • Grouping and partitioning.

The arguments passed into the collect() method are called collectors which are implementations of Collector interface.

In this tutorial, I am going to show you that, how to use these collectors under each category with examples. Very famous example, Employee and Department and the sample data set is as follows.

public class Employee {
 
    private String name;
 
    private Integer age;
 
    private String city;
 
    private Integer salary;
 
    private String sex;
 
    private Department department;
 
    public Employee(String name, Integer age, String city, Integer salary, String sex, Department department) {
        super();
        this.name = name;
        this.age = age;
        this.city = city;
        this.salary = salary;
        this.sex = sex;
        this.department = department;
    }

   // getters and setters
 
}


public class Department {

    private String departmentName;
 
    private Integer noOfEmployees;

    public Department(String departmentName, Integer noOfEmployees) {
         super();
         this.departmentName = departmentName;
         this.noOfEmployees = noOfEmployees;
    }
    // getters and setters
}


Sample data set :
Department account = new Department("Account", 75); 
Department hr = new Department("HR", 50);
Department ops = new Department("OP", 25);
Department tech = new Department("Tech", 150);
  
List<Employee> employeeList = Arrays.asList(new  Employee("David", 32, "Matara", 2000, "Male", account), 
    new  Employee("Brayan", 25, "Galle",  3000, "Male",hr),
    new  Employee("JoAnne", 45, "Negombo",  800, "Female", ops),
    new  Employee("Jake", 65, "Galle",  2500, "Male", hr),
    new  Employee("Brent", 55, "Matara",  8000, "Male", hr),
    new  Employee("Allice", 23, "Matara",  600, "Female", ops),
    new  Employee("Austin", 30, "Negombo",  9000, "Male", tech),
    new  Employee("Gerry", 29, "Matara",  2500, "Male", tech),
    new  Employee("Scote", 20, "Negombo",  2500, "Male", ops),
    new  Employee("Branden", 32, "Matara",  2500, "Male", account),
    new  Employee("Iflias", 31, "Galle",  2900, "Female", hr)); 



Combining Elements

The most straightforward and frequently used collector is toList() static method. By using this method, elements of a Stream can be combined to a List as follows.

List<Employee> employeeList = employeeList.stream().collect(Collectors.toList()); 

Similarly, if you want to convert Stream into a set, the toSet() static method can be used as follows.

Set<Employee> employeeSet = employeeList.stream().collect(Collectors.toSet());


Reducing and Summarizing


The collectors which are parameters to the Stream's collect() method can also be used to combine all the items in the stream into a single result like total, min, max and count.

Using Collectors.counting() :

If we want to find the number of employees in the list, Collectors.counting() method can be passed as an argument to collect() method as follows.
Long totalNumOfEmployees = employeeList.stream().collect(Collectors.counting());

Using Collectors.maxBy() and Collectors.minBy() :

If we want to find the maximum and the minimum value in a stream, we can use maxBy() and minBy() methods. These two collectors take a Comparator as an argument to compare the elements in the stream.

Let's try to find the employee who is getting the maximum and the minimum salary.

//Find max salary employee 
employeeList.stream() 
            .collect(Collectors.maxBy(Comparator.comparingInt(Employee::getSalary))) 
            .ifPresent(e -> System.out.println("Max salary employee :" + e.getName())); 

//Find min Salary 
employeeList.stream() 
            .collect(Collectors.minBy(Comparator.comparingInt(Employee::getSalary))) 
            .ifPresent(e -> System.out.println("Min salary employee :" + e.getName()));


Using Collectors.summingXXX() methods:


Collectors.summingInt(), Collectors.summingLong() and Collectors.summingDouble() are some collectors which can be used to get the summation of a particular properly of elements in a stream. These methods accept a property of corresponding type in order to perform the summing operation.

Let's say, we want to find the total salary of all the employees.

Integer totalSalary = employeeList.stream().collect(Collectors.summingInt(Employee::getSalary)); 



Using Collectors.averagingXXX() methods:

Collectors.averagingInt(), Collectors.averagingDouble() and Collectors.averagingLong() are the collectors that can be used to calculate the average value of some numeric fields. Let's say, we want to find the average salary of all the employees.

Double averageSalary = employeeList.stream().collect(Collectors.averagingInt(Employee::getSalary));

Using Collectors.summarizingXXX() methods:

Quite often, we might need to get the count, total, maximum, minimum and average by using a single operation. Collectors.summarizingInt(), Collectors.summarizingDouble() and Collectors.summarizingLong() collectors can be used to get above all statistics from a single operation. 

Let's try to get all above salary statistics from a single operation.

IntSummaryStatistics salalryStat = employeeList.stream().collect(Collectors.summarizingInt(Employee::getSalary));

IntSummaryStatistics class has many defined methods which returns the above statistic values separately. The string representation of above IntSummaryStatistics result will be as follows.

IntSummaryStatistics{count=11, sum=36300, min=600, average=3300.000000, max=9000}

Using Collectors.joining() method:

The joining() method can be used to concatenate String type property values into a single string from the elements of a stream. Note that the joining() method internally makes use of a StringBuilder to append the generated string into one.


Let's say, we want to get all the employee's names as comma separated single string.

String employeeNameStrings = employeeList.stream().map(Employee::getName).collect(Collectors.joining(", ")); 
System.out.println(employeeNameStrings);

Using Collectors.reducing() method:

The reducing() method is a generalization of all the collector methods. Using the reducing() method, we can perform many operations that were performed using specific collector methods. These operations include max, min, sum etc. 

The reducing() method takes three arguments.


  • The first argument is the starting value for reduction operation. This value will return in case empty stream. 
  • The second argument is usual transformation function. 
  • The third argument is a BinaryOperator that aggregates two arguments into a single one. 


Let's try to get the maximum and minimum salary of employees by using reducing() method too.

//Max salary
Integer maxSalary = employeeList.stream().collect(Collectors.reducing(0, Employee::getSalary, Integer::max)); 

Optional minSalary = employeeList.stream().collect(Collectors.reducing((e1, e2) -> e1.getSalary() < e2.getSalary() ? e1 :  e2));
System.out.println("Min salary " + minSalary.get().getSalary()); 

The reducing() method has a form of accepting single argument as in the above min operation.


Grouping

Groping of elements based on a one or more property is a very common database operation. The Collectors.groupingBy() factory method in Java 8 can be used to perform same kind of operations with collections of elements. The Collectors.groupingBy() method accepts a method reference as an argument which is used to classify the elements of the stream into different groups. 

Let's try to group list of employees by their department.

Map<Department, List<Employee>> employeesOverDepartment = employeeList.stream().collect(Collectors.groupingBy(Employee::getDepartment));

We call the above Function a classification function, because it is used to classify the elements of the stream into different groups. But, it is not always possible to use a classification function, because we may wish to classify the elements using more complex criteria than a simple property reference. 

The Collectors.groupingBy() method accepts a lambda expression which allows us to group elements based on more complex criteria. 

Let's say, we want to group our employee list into two groups as "High Salary" and "Low Salary". This might not be a good example, but just to show you, how we can use more complex criteria to group elements.

Map<String, List<Employee>> map = employeeList.stream().collect(
                                        Collectors.groupingBy((Employee e) -> {
                                                if (e.getSalary() > 2000) {
                                                   return "High Salary";
                                                } else {
                                                    return "Low Salary";
                                                } 
                                        }));

The similar approach can be used to group employees by their department name.

Map<String, List<Employee>> employeesByDepartmentName = employeeList.stream()
                       .collect( Collectors.groupingBy((Employee e) -> { 
                                    return e.getDepartment().getDepartmentName();
                               }));


The Collectors.groupingBy() method has a version of accepting two argument which allows us to perform multilevel grouping. In the above example, we grouped employees by the department name. 

Let's say, we want to further group employees within a particular department by their sex.

Map<String, Map<String, List<Employee>>> employeesByDepartmentName = employeeList.stream()
                .collect( Collectors.groupingBy((Employee e) -> { return e.getDepartment().getDepartmentName(); },
                         Collectors.groupingBy((Employee e) -> { return e.getSex(); }) ) );


In the above example, two-argument version of Collectors.groupingBy() method, the second argument was a another groupingBy() method. But, it is not necessarily to be a groupingBy() method. But, more generally, the second collector passed to the first groupingBy() method can be any type of collector.

Let's say, we want to get the number of employees in each department.

Map<String, Long> noOfEmployeesInDepartment = employeeList.stream()
                       .collect(Collectors.groupingBy(
                                 (Employee e) -> { return e.getDepartment().getDepartmentName(); }, 
                                  Collectors.counting()));
Using a similar kind of approach, we can find the employee who gets the maximum salary for each department. See the following example.
Map<String, Employee> maxSalaryEmployeeOfDept = employeeList.stream()
                 .collect(Collectors.groupingBy((Employee e) -> {return e.getDepartment().getDepartmentName();}, 
                          Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Employee::getSalary)), 
                                                                        Optional::get) ) );




More generally, the collector passed as the second argument to the groupingBy() factory method will be used to perform a further reduction operation on all the elements in the stream classified into the same group. Let's say, we want to get the total salary for each department.
Map<String, Integer> totalSalaryOverDepartment = employeeList.stream() 
            .collect(Collectors.groupingBy(
                         (Employee e) -> {return e.getDepartment().getDepartmentName();}, 
                          Collectors.summingInt(Employee::getSalary)));

Partitioning

Sunday, July 15, 2018

Spring Boot Complete Example

This post provides a complete example of using spring boot to develop a loosely coupled REST service. Using spring boot, we can develop production ready java application which will run independently, as stand alone application with minimal dependencies and less coupling with other applications. Most of the time, spring boot application will provide a cohesive service and the boundaries of the service is clearly defined. Let's deep into the our example.

For this tutorial, I am going to use Spring Boot 2.0.3.RELEASE which requires the java 8 or 9 and Maven 3.2+, Eclipse as an IDE.

Creating Maven jar module

Since, we are going to bundle our application as a .jar file, we can use eclipse IDE support to generate maven jar module after integrating maven with Eclipse IDE. The recent version of Eclipse comes with integrated maven plugin. So you don't need to explicitly add maven plugin into eclipse. I am not going to explain, how to create maven jar module with eclipse within this post. If you want to know it, you can read my another post here which clearly explains about creating maven modules with eclipse. If you create a maven project by using 'quickstart' artifact, you will get a project structure similar to the following.



I have created a maven module called 'customer' with 'com.semika' as groupId and 'customer' as artifact id. You can choose what ever package structure, you want. Your inner package structure will change based on it.

App.java and AppTest.java files will be removed soon. Have a look on pom.xml file which contains information about the project and configuration details used by Maven to build the project. You can remove Junit dependency for now, since this tutorial does not cover unit testing features.

I want to highlight one important element here. 

<packaging>jar</packaging>

This is where, we tell maven to bundle our application as a .jar file for deployment. 


Adding spring boot features

Now, what we have is, typical maven jar module. How we are going to convert this into spring boot application? 

All spring boot dependencies are defined under org.springframework.boot group id within the maven repository. The spring-boot-starter-parent is a project which has some default settings and basic required configurations that we can use in order to quickly start using spring boot. We can inherits these default settings by adding following element into our pom.xml file.

<!-- Inherit defaults from Spring Boot --> 
<parent> 
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId> 
    <version>2.0.3.RELEASE</version> 
</parent>

Connecting to a database

Now, let's see, how we can connect our spring boot application with a database or how we integrate a data source to our spring boot application. Spring framework provides a great support for accessing a database from direct JDBC access to ORM technologies like Hibernate. 

The javax.sql.DataSource interface provides standard methods in order to work with a database by creating data source with a connection pool. There are several implementation like BoneCP, Apache common DBCP2 and spring's default HikariCP.If We use the spring-boot-starter-jdbc or spring-boot-starter-data-jpa “starters”, we automatically get a dependency to HikariCP. We are going to use 'spring-boot-starter-data-jpa' for data access later on this tutorial. 

Now, time has come to add 'application.properties' file to our project. In spring boot application, this file contains all the configuration properties and the file should be available on classpath. I am going to delete 'App.java' and 'AppTest.java' file and create a new folder as 'resources' inside the 'main' folder, parallel to 'java' folder. When building modules by using maven, the files inside the 'resources' folder are made available to classpath. We don't need to do any extract things. 

Let's create a file as 'application.properties' inside the resources folder. I am going to connect my spring boot application to a MySql database. The minimal properties required to create a datasoruce for spring boot application, are as follows. 

spring.datasource.url=jdbc:mysql://localhost/springboot?useSSL=false
spring.datasource.username=root
spring.datasource.password=abc123
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.show-sql=false

Here property name convention was not selected randomly. Spring's datasource configuration properties should be prefix with spring.datasource.*. If you want to set up a specific data source implementation, the property names should be specify with respective prefix as spring.datasource.hikari.*, spring.datasource.tomcat.*, spring.datasource.dbcp2.*.

Since we are going to connect to MySql database, mysql-java connector maven dependency should be added to our pom.xml file as follows.

<dependency> 
     <groupId>mysql</groupId> 
     <artifactId>mysql-connector-java</artifactId> 
</dependency>

Adding Main Application class

Every spring boot application should have a main class with main() method defined. Generally this class is named as "Application.java" and should locate in the root package above the other classes. This class is normally annotated with few annotations. 

  • @EnableAutoConfiguration - This annotation enables auto-configuration for our spring boot application which attempts to automatically configure our Spring application based on the jar dependencies that we have added. 
  • @ComponentScan - This enables spring bean dependency injection feature by using @Autowired annotation. All of our application components which were annotated with @Component, @Service, @Repository or @Controller are automatically registered as Spring Beans. These beans can be injected by using @Autowired annotation. 
  • @Configuration - This enables Java based configurations for spring boot application. Usually the class that defines the main method is a good candidate to annotate with this annotation.

I am going go create a new class as "Application.java" inside the com.semika package, which is the root for my spring boot application.

package com.semika;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@EnableAutoConfiguration
@ComponentScan
@Configuration

public class Application {

    public static void main(String[] args) {
         SpringApplication app = new SpringApplication(Application.class);
         app.run(args); 
    }
}

In stead of using all three annotation, we can use only @SpringBootApplication annotation which is equivalent to using @Configuration, @EnableAutoConfiguration, and @ComponentScan with their default attributes, as shown in the following example.

package com.semika;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@SpringBootApplication
public class Application {
     public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args); 
     }
}

Adding data access features with spring-data-JPA

Now, let's see, how we can integrate data access features to our spring boot applications. Data access classes are called 'Repositories' in spring boot application. JPA (Java Persistence API) is a standard technology that lets us “map” objects to relational databases. 

The spring-boot-starter-data-jpa starter project provides a quick way to get started with data access for spring boot application. It provides the following key dependencies: 
  • Using Hibernate to map objects with database tables. 
  • Spring Data JPA which can be used to write JPA-based repositories. 
  • Core ORM support from the Spring Framework.
In order to add data access features to our spring boot application, we should add the following maven dependency to our pom.xml file. After adding bellow dependency, we can use usual JPA annotations to map objects with relational database table. 

<dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-data-jpa</artifactId> 
</dependency>

Let's create a new package as 'customer' inside the root folder which is com.semika where the 'Application.java' class is located by now. Inside customer folder, new entity class is crated as 'Customer.java'. For now, my customer database table has three attributes as follows.











package com.semika.customer;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="customer") 
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
 
    @Column(name="first_name")
    private String firstName;
 
    @Column(name="last_name")
    private String lastName;
 
    @Column(name="address")
    private String address;

    public Customer() {
       super();
    }

    // getters and setters
}

Spring data JPA repositories are interfaces that can be defined to access data. JPA queries are created automatically from the method names. For example, findAll() method in 'CustomerRepository.java' class fetches all the customers. The findByFirstName(String firstName) method will fetch all the customers for a given first name. 

The central interface in the Spring Data repository abstraction is Repository interface. It takes the domain class to manage as well as the ID type of the domain class as type arguments.The CrudRepository interface provides sophisticated CRUD functionality for the entity class that is being managed. Our repository interfaces should extend from CrudRepository interface.

Our 'CustomerRepository.java' interface will be as follows:

package com.semika.customer;

import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer Long> {

}


You may find for the implementation class? Spring data JPA provides the implementation for most of the data access scenarios. We don't need to implement explicitly those methods. If you want to read more about spring data JPA, you can read the reference documentation here.

Further, I am going to add 'CustomerService.java' interface and it's implementation 'CustomerServiceImpl.java' class in order to keep our business logic in a separate layer.

package com.semika.customer;

public interface CustomerService {
    public Iterable<Customer> findAll(); 
}


package com.semika.customer;
package com.semika.customer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class CustomerServiceImpl implements CustomerService {

    @Autowired
    private CustomerRepository customerRepository;
 
    public Iterable<Customer> findAll() {
        return customerRepository.findAll(); 
    }
}

You can see that,CustomerRepository is injected to CustomerServiceImpl class using @Autowired annotation. We did enable this by adding @ComponentScan annotation via @SpringBootApplication to our 'Application.java' class early in this tutorial.

Adding web features

Now, it's time to build and test our application. Suppose, client makes HTTP requests to fetch all the customers data. So our spring boot application should response to HTTP requests. Spring MVC provides 'Controllers' which accepts HTTP requests and responses to those. Here, we are going to add some spring MVC features to our spring boot application. By using spring-boot-starter-web project, we can integrate some basic MVC features to our spring boot application so that we can write simple Controller class which responses client's HTTP requests.

We should add the following maven dependency to our project.

<dependency> 
     <groupId>org.springframework.boot</groupId> 
     <artifactId>spring-boot-starter-web</artifactId> 
</dependency>

package com.semika.customer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CustomerController {

    @Autowired
    private CustomerService  customerService;
 
    @RequestMapping("/customers") 
    @ResponseBody
    public Iterable<Customer> findAll() {
       Iterable<Customer> customers = customerService.findAll();
       return customers;
    }
}

@RestController is a stereotype annotation in Spring MVC which provides hints for people reading the code and for Spring that the class plays a specific role. That is, it contains the gates to enter into the application. In this case, our class is a web @Controller, so Spring considers it when handling incoming web requests. The @RestController annotation tells Spring to render the resulting string directly back to the caller.

The @RequestMapping annotation provides “routing” information. It tells Spring that any HTTP request with the '/customers' path should be mapped to the findAll() method.

These two annotations are spring MVC annotations. They are not specific to spring boot. We added this spring MVC web features in order to test our application by making some web requests. With the adding of 'spring-boot-starter-web' to a spring boot application, when running it, spring boot application starts up it's own web container and runs with in it.

So now, our project structure should be as follows.


Building application

Spring boot jar file is called a self-contained executable jar file that we can run directly in production environment. Executable jars are archives containing your compiled classes along with all of the jar dependencies that your code needs to run. In our example, since we used 'spring-boot-starter-web', when running the jar file, it starts internal web container in order to run the application. 

To create an executable jar, we need to add the spring-boot-maven-plugin to our pom.xml. To do so, insert the following lines just below the plugins section.

<plugins> 
     <plugin> 
          <groupId>org.springframework.boot</groupId> 
          <artifactId>spring-boot-maven-plugin</artifactId> 
     </plugin> 
</plugins>

You might notice that some of the configurations for above plugin is missing here. Since we are using 'spring-boot-starter-parent', we don't need to worry about those, because those are already included within the parent project. For example, parent project POM includes <executions> configuration to bind the repackage goal.

Let's look at our final pom.xml file now:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.semika.user</groupId>
  <artifactId>customer</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>
  
  <!-- Inherit defaults from Spring Boot -->
  <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.0.3.RELEASE</version>
  </parent>

  <name>customer</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  
  <!-- Building an executable jar file -->
  
  <build>
      <plugins>
         <plugin>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
  </build>

  <dependencies>
        <!-- Adding spring data jpa features -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
 
        <!-- Java MySQL connector -->
        <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
        </dependency>
 
        <!-- Integrating spring MVC features -->
        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  </dependencies>
</project>


Let's build the application. Go the project root folder where pom.xml file is located and run the following command.

mvn package

Inside the target folder, you can see our executable jar file created as 'customer-0.0.1-SNAPSHOT.jar'.

Running the application:

From the same folder, run the following command to run the jar file. 

java -jar target/customer-0.0.1-SNAPSHOT.jar

If you analyze the logs when starting up the application, you can discover many important things. The console output at the server starting is as follows:


If you see the logs near the bottom, it starts the Tomcat server on port 8080. If you access the http://localhost:8080/customers URL from the browser, you will get JSON response of customers as the response.

If you want to start the application on different port than the default one, you can specify the port by suing --server.port option as follows.

java --server.port=9000 -jar target/customer-0.0.1-SNAPSHOT.jar

If you want to start the application with debug enabled, you can use the following command:

java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n -jar target/customer-0.0.1-SNAPSHOT.jar

To start the application with debug log enabled, you can use the following command

java -jar target/customer-0.0.1-SNAPSHOT.jar --debug

If you want to specify the sever running port in 'application.properties' file, you can include the following property into the file. 

server.port=${port:9000}

With above entry in 'application.properties' file, in stead of using --server.port option, you can simply user --port option with java -jar command in order to specify the port.

Most of the time, your configuration properties are different from environment to environment. For environment like, development, production and testing, you might need to keep different set of configuration properties. You can do this by keeping different configuration profiles for each environment. You should create the configuration properties file in the following format in order to achieve this.

application-${profile}.properties

Let's say you need to keep two configuration profiles for 'development' and 'production' environment separately. In this case, you should create two property files as 'application-development.properties' and 'application-production.properties'. When starting the application using java -jar command, you should specify the profile with -D parameter as follows: 

java -jar -Dspring.profiles.active=production customer-0.0.1-SNAPSHOT.jar

I hope this post will be helpful  specially for beginners who try to learn about spring boot application and Micro services.

References : Spring Boot Reference Guide

Thursday, July 5, 2018

Java 8 Stream examples

This post will help you to understand some of the important and frequently used Stream operations in Java 8 which makes your programming with Java easy.

Let's take our traditional example, Employee and Department.  

public class Employee {
 
 private String name;
 
 private Integer age;
 
 private String city;
 
 private Department department;
 
 public Employee(String name, Integer age, String city, Department department) {
    this.name = name;
    this.age = age;
    this.city = city;
    this.department = department;
 }

 // getters and setters.

}


public class Department {

 private String departmentName;
 
 private Integer noOfEmployees;

 public Department(String departmentName, Integer noOfEmployees) {
      this.departmentName = departmentName;
      this.noOfEmployees = noOfEmployees;
 }

        // getters and setters
}

I am going to have some sample data set as follows in order to show you some important functionalities of Java 8 Stream interface. We have four departments and set of employees from those departments.

      Department account = new Department("Account", 75); 
      Department hr = new Department("HR", 50);
      Department ops = new Department("OP", 25);
      Department tech = new Department("Tech", 150);          
  
      List<Employee> employeeList = Arrays.asList(new  Employee("David", 32, "Matara", account), 
                           new  Employee("Brayan", 25, "Galle", hr),
                           new  Employee("JoAnne", 45, "Negombo", ops),
                           new  Employee("Jake", 65, "Galle", hr),
                           new  Employee("Brent", 55, "Matara", hr),
                           new  Employee("Allice", 23, "Matara", ops),
                           new  Employee("Austin", 30, "Negombo", tech),
                           new  Employee("Gerry", 29, "Matara", tech),
                           new  Employee("Scote", 20, "Negombo", ops),
                           new  Employee("Branden", 32, "Matara", account),
                           new  Employee("Iflias", 31, "Galle", hr)); 

Find all employees who lives in 'Matara' city, sort them by their name and print the names of employees.

employeeList.stream()
     .filter(e -> e.getCity().equalsIgnoreCase("Matara"))
     .sorted(Comparator.comparing(Employee::getName))
     .forEach(e -> System.out.println(e.getName()));

Find distinct department names that employees work for.

employeeList.stream()
            .map(e -> e.getDepartment().getDepartmentName())
            .distinct()
            .forEach(System.out::println); 


Find the department names that these employees work for, where the number of employees in the department is over 50.

employeeList.stream()
            .map(Employee::getDepartment)
            .filter(d -> d.getNoOfEmployees() > 50)
            .distinct()
            .forEach(d -> System.out.println(d.getDepartmentName()));


Create a comma separate string of department names sorted alphabetically.

String s = employeeList.stream()
                       .map(e -> e.getDepartment().getDepartmentName())
                       .distinct()
                       .sorted()
                       .reduce("", (a, b) -> (a + "," + b)); 
System.out.println(s); 


Are there any employees from HR Department?

if (employeeList.stream()
                .anyMatch(e -> e.getDepartment().getDepartmentName().equalsIgnoreCase("HR"))) { 
    System.out.println("Found employees frm HR department"); 
}


Print all employee's name who are working for account department.

employeeList.stream()
            .filter(e -> e.getDepartment().getDepartmentName().equalsIgnoreCase("Account"))
            .map(Employee::getName)
            .forEach(System.out::println);


What is the highest number of of employees in all departments?

employeeList.stream()
            .map(e -> e.getDepartment().getNoOfEmployees())
            .reduce(Integer::max)
            .ifPresent(System.out::print);


Find the department which has the highest number of employees.

employeeList.stream()
            .map(Employee::getDepartment)
            .reduce( (d1, d2) -> d1.getNoOfEmployees() > d2.getNoOfEmployees() ? d1 : d2)
            .ifPresent(d -> System.out.println(d.getDepartmentName()));

The same thing can be done as follows using the max() method.

employeeList.stream()
            .map(Employee::getDepartment)
            .max(Comparator.comparing(Department::getNoOfEmployees))
            .ifPresent(d -> System.out.println(d.getDepartmentName()));


Find the total number of employees in all the departments.

employeeList.stream()
            .map(e -> e.getDepartment())
            .distinct()
            .map(e-> e.getNoOfEmployees())
            .reduce(Integer::sum).ifPresent(System.out::println);

Wednesday, July 4, 2018

Java 8 anyMatch(), allMatch(), noneMatch(), findAny() examples

Finding the existence of some elements among a collection of objects after matching with a specific property is common data processing idiom in programming. The Java 8 Streams API provides such facilities through the allMatch, anyMatch, noneMatch, findFirst, and findAny methods of a stream.

Let's take the following class to write example program for each of these methods. This class represents Dish of a menu.

public class Dish {

    private String name;
    private Boolean vegitarian;
    private Integer calaries;
    private Type type;
 
    public Dish(String name, Boolean vegitarian, Integer calaries, Type type) {
       super();
       this.name = name;
       this.vegitarian = vegitarian;
       this.calaries = calaries;
       this.type = type;
    }

    public String getName() {
       return name;
    }

    public void setName(String name) {
       this.name = name;
    }

    public Boolean getVegitarian() {
       return vegitarian;
    }

    public void setVegitarian(Boolean vegitarian) {
       this.vegitarian = vegitarian;
    }

    public Integer getCalaries() {
       return calaries;
    }

    public void setCalaries(Integer calaries) {
       this.calaries = calaries;
    }

    public Type getType() {
       return type;
    }

    public void setType(Type type) {
       this.type = type;
    }

    public enum Type { MEAT, FISH, OTHER };
}

Using anyMatch() method 

The anyMatch() method accepts a Predicate instance and checks for any matching elements in the stream. This method returns a boolean value, true, if it found a matching, otherwise falseThe anyMatch() method will traverse through the elements of the Stream until it finds a match.

Let's say, we have a  list of Dishes and want to find out if there is any vegetarian Dish among those. Just remind, how did you do this before Java 8. You had to at least write 5 or 6 lines of codes to do the above. In Java 8, you can do this by a single line.

List<Dish> menu = ....
if (menu.stream().anyMatch(Dish::getVegitarian)) {
    System.out.println("The menu is (somewhat) vegetarian friendly!!");
}

Using allMatch() method 

The allMatch() method also accepts a Predicate instance and works similar to anyMatch() method. This method will check to see if all the elements of the stream match the given predicate. For example, you can use it to find out whether the menu is healthy (that is, all dishes are below 1000 calories).

boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000);

The allMatch() method stops traversing the elements of the stream as soon as one element produces false output.

Using noneMatch() method

The opposite of allMatch() is noneMatch(). It ensures that no elements in the stream match the given predicate. For example, you could rewrite the previous example as follows using noneMatch.

List<Dish> menu = ....
boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);

Using findAny() method

The findAny() method returns an arbitrary element of the current stream. It can be used in conjunction with other stream operations. For example, you may wish to find a dish that’s vegetarian. You can combine the filter method and findAny to express this query.

List<Dish> menu = .....
Optional<Dish> dish = menu.stream().filter(Dish::isVegetarian).findAny();

The Optional<T> class (java.util.Optional) is a container class to represent the existence or absence of a value. In the previous code, it’s possible that findAny() doesn’t find any element. Instead of returning null, which is well known for being error prone, the Java 8 introduced Optional.

Optional interface has few important method as follows,

          isPresent() method which returns true if Optional contains a value, false otherwise.
          ifPresent(Consumer<T> block) executes the given block if a value is present.

if (dish.isPresent()) {
 ///........
}

dish.ifPresent(d->System.out.println(d.getName()); 

Java 8 map(), flatMap() examples

Using map() method

When programming, it is very common, processing data in order to collect some information from a collections of objects. Let's say, we wanted find out the cities from all the employees in a particular company. Our employee class will be as follows. 


public class Employee {
 
    private String name;
    private Integer age;
    private String city;
    private String state; 
    private Department department;
 
    public String getCity() {
         return city;
    }

    public void setCity(String city) {
         this.city = city;
    } 

    public String getState() {
         return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

I didn't include all the attributes for Employee class, but what I need 'city' attribute in this case. 

So now, we have a list of Employee objects and need to find out distinct cities. Let's see the approach before Java 8. Hopefully, you will write a code as follows in order to get distinct cities. 

List<Employee> employeeList = .....
Set<String> cities = new HashSet<String>();
for (Employee emp : employeeList) {
    cities.add(emp.getCity());
}

Java 8 Stream interface introduces map() method which takes a function as an argument. This function is applied to each element in the stream and returns new stream. The code will look like follows.

List<Employee> employeeList = new ArrayList<Employee>();
List<String> cities = employeeList.stream()
                                  .map(Employee::getCity)
                                  .distinct()
                                  .collect(Collectors.toList());

Using flatMap() method 

Java 8 Stream interface introduces flatMap() method which can be used to merge or flatten few streams into a single stream.

Let's take an example. Suppose, we wanted to filter out distinct words in a text file. Look at the following text file.


Sri Lanka is a beautiful country in Indian ocean.
It is totally surrounded by the sea.

In Java 8, we can read a text file using a single line and it will return a Stream of string. Each element of the stream will be a one line of the text file.

Stream<String> lineStream = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());

If you see the out put of above code by printing 'lineStream' Stream, it will be the lines of the text file. 

Next, we can convert each element of the above Stream into a Stream of words. Then we can use flatMap() method to flatten all Streams of words into a single Stream. If we execute the following code for each element of the 'lineStream' Stream, we will get two Stream of words. See the following code.

line -> Arrays.stream(line.split(" "))

Two Streams of words will be as follows.

Stream 1 : [SriLanka][is][a][beautiful][country][in][Indian][ocean.]} 
Stream 2 : [It][is][totally][surrounded][by][the][sea.]

The flatMap() method can flatten these two into a single Stream of word as follows.

Stream<String> wordStream = lineStream.flatMap(line -> Arrays.stream(line.split(" ")));

If you print the elements of the above 'wordStream', it will be all the words of the text file. But still, you will see duplicate words. You can use distinct() method to avoid the duplicates. Here's the final code.

List<String> wordStream = lineStream.flatMap(line -> Arrays.stream(line.split(" ")))
                                    .distinct()
                                    .collect(Collectors.toList());

If you see closely, you can find the distinct words of a text file just by using two lines of code with Java 8.
Share

Widgets