1. @BeforeAll, @AfterAll – Runs Before and After Class and should be static
  2. @DisplayName – Displays description about the Test Method
  3. @Disabled – @Ignore in Junit 4. Disables the Test Method
  4. @Nested – Helps in grouping of similar test methods together
  5. @ParameterizedTest – Supplying more than one input for same method in row using array
  6. @ValueSource – Provides multiple paramters to same test method for ParameterizedTest
  7. @CsvSource – Provides multiple paramters to same test method for ParameterizedTest in Key Value Pair. Key is Input and Value is expected Output

AccountUtils.java

import java.io.IOException;

public class AccountUtils {

    public boolean validateAccountId(String acctNo) throws IOException {
        if (getAccountIDLength(acctNo)) {
            return true;
        } else {
            throw new IOException("Account ID is Invalid");
        }
    }

    public boolean getAccountIDLength(String acctNo)
    {
        if(acctNo.length() < 5) return false;
        if(acctNo.length() > 10) return false;
        if(!acctNo.contains("-")) return false;

        return true;
    }

    public  String getFormattedAccID(String accountNo){
        return accountNo.toUpperCase();
    }

    public String[] getBankDetails(String accountNo){
        String[] arrAccountDetails = accountNo.split("-");
        return arrAccountDetails;
    }
}

AccountUtilsTest.java

import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.fail;

public class AccountUtilsTest {

    AccountUtils systemUnderTest = new AccountUtils();

    @BeforeAll
    static void init(){
        System.out.println("Initilize connection to Database");
    }

    @AfterAll
    static void destroy(){
        System.out.println("Deallocate connection to Database");
    }

    @Test
    @DisplayName("AccountID Length Validation Success")
    void test_getAccountIDLength_Success(){
        boolean expectedOutput = true;
        boolean actualOutput = systemUnderTest.getAccountIDLength("SBI-104526");

        Assertions.assertEquals(expectedOutput, actualOutput);
        Assertions.assertTrue(actualOutput);
    }

    @Test
    @Disabled
    void test_validateAccountId_Success() throws IOException {
        Assertions.assertTrue(systemUnderTest.validateAccountId("SBI-104526"));
    }

    @Test
    @DisplayName("AccountID is Invalid ")
    void test_validateAccountId_Exception(){
        Assertions.assertThrows(IOException.class, ()->{
            systemUnderTest.validateAccountId("");
        });
    }

    @Test
    @DisplayName("Account Bank Details Validation")
    void test_AccountIDForBankName_Success(){
        String[] expectedOutput = new String[]{"SBI", "104526"};
        String[] actualOutput = systemUnderTest.getBankDetails("SBI-104526");

        Assertions.assertArrayEquals(expectedOutput, actualOutput);
    }

    @Nested
    class checkValidAccountId
    {
        @ParameterizedTest
        @ValueSource(strings={"SBI", "SBI-123456789", " ", ""})
        @DisplayName("Parameterized Test AccountID Length Validation Failiure")
        void test_getAccountIDLength_Failiure(String accountId){
            Assertions.assertFalse(systemUnderTest.getAccountIDLength(accountId));
        }

        @ParameterizedTest(name="AccountID {0} formatted to {1}")
        @CsvSource(value={"sbi-123456, SBI-123456", "cbi-123456, CBI-123456"})
        @DisplayName("AccountID Format Validation")
        void getFormattedAccID_Success(String inputString, String expectedOutput){
            Assertions.assertEquals(expectedOutput, systemUnderTest.getFormattedAccID(inputString));
        }
    }
}

What is BDD?

  Given some preconditions (Arrange) -> When an action occurs (Act) -> Then verify the output (Assert)

I.E.

  Given three items in Cart
  When one Item is deleted
  Then Two items should be left in Cart
@ExtendWith(MockitoExtension.class)
public class EmpServiceImplTest {

    @Mock
    EmployeeDB employeeDB;

    @InjectMocks
    EmpServiceImpl empServiceImpl;

    @Test
    @DisplayName("Check Employee in BDD Style")
    public void test_getEmployees_Success(){
        //Given - Employee List has only one row with List size as 1
        //When  - New Employee is added to the List
        //Then  -  Employee List should contain two rows

        //Given - Employee List has 3 Employees with List size as 3
        List<Employee> arrEmployees = new ArrayList<>();
        arrEmployees.add(new Employee("101", "Mugil"));
        arrEmployees.add(new Employee("102", "Max"));
        arrEmployees.add(new Employee("103", "Mani"));

        given(employeeDB.getEmployeesFromDB()).willReturn(arrEmployees);

        //When
        List<Employee> arrFilteredEmps = empServiceImpl.getEmployees();

        //Then - there should 3 EmployeegetEmployeeByIdFromDB
        Assertions.assertEquals(3, arrFilteredEmps.size());
    }
}
  1. What is Difference between @Injectmock and @Mock?
    @Mock creates a mock. @InjectMocks creates an instance of the class and injects the mocks that are created with the @Mock (or @Spy) annotations into this instance.
    Note you must use @RunWith(MockitoJUnitRunner.class) to initialize these mocks and inject them (JUnit 4).With JUnit 5, you must use @ExtendWith(MockitoExtension.class).

    @RunWith(MockitoJUnitRunner.class) // JUnit 4
    // @ExtendWith(MockitoExtension.class) for JUnit 5
    public class SomeManagerTest {
    
        @InjectMocks
        private SomeManager someManager;
    
        @Mock
        private SomeDependency someDependency; // this will be injected into someManager
     
         //tests...
    }
    

  2. When should I use spy vs Mock?
    Use @Mock when you want to just test the functionality externally without actually calling that method. Use @Spy when you want to test the functionality externally + internally with the very method being called. Spy is a combination of Mock(methodInterceptors) and InjectMock(Instance of class Tested).
  3. What happens when unstubbed method called?
    Mockito will return null or primitive value or empty collection
  4. Does mockito when accepts combination of specific and generic matchers?
    No. Mockito matchers should be either generic or specific.
  5. What is BDD?
    Behaviour Driven Development (Given Scenario -> When the action happens -> Then this should be the case)

    Given 3 Items in Cart ->  When One Item gets Deleted -> Then 2 Items would be in Cart
    
  6. How to Verify whether the Service or method call has been made?
    Verify is used to check whether the call to the method is done or not.

    .
    .
     Mockito.verify(employeeDB, times(1)).getEmployeeByIdFromDB("101");
     Mockito.verify(employeeDB, never()).getEmployeesFromDB();
     Mockito.verify(employeeDB, atleast(2)).getEmployeeByIdFromDB("101");
    .
    .
    
  7. How to check the arguments passed to the method?
    ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
    verify(mock).doSomething(argument.capture());
    assertEquals("John", argument.getValue().getName());
    

    (or)

    //capturing varargs:
    ArgumentCaptor<Person> varArgs = ArgumentCaptor.forClass(Person.class);
    verify(mock).varArgMethod(varArgs.capture());
    List expected = asList(new Person("John"), new Person("Jane"));
    assertEquals(expected, varArgs.getAllValues());
    
  8. @SpringBootTest vs @runwith(springrunner.class) or @ExtendWith
    Enables spring boot features like @Autowire, @MockBean etc.. during junit testing. This is used to provide a bridge between Spring Boot test features and JUnit. Whenever we are using any Spring Boot testing features in our JUnit tests, this annotation will be required.@RunWith is an old annotation from JUnit 4 to use test runners. If you’re using JUnit 5 (Jupiter), you should use @ExtendWith to use JUnit extensions

    @SpringBootTest : This annotation is used to load complete application context for end to end integration testing.The @SpringBootTest annotation can be used when we need to bootstrap the entire container. The annotation works by creating the ApplicationContext that will be utilized in our tests. So this would be the annotation used while testing bootstrap class of Spring Boot Application. load application.properties and give me all the Spring Boot goodness

    We pass list of classes as parameter to @SpringBootTest whose configs are loaded from application.properties when the application is bootstrapped.

    RestWithSpringBootApplicationTests.java

    @SpringBootTest(classes = {RestWithSpringBootApplication.class, EmployeeMgmtConfig.class})
    class RestWithSpringBootApplicationTests {
      @Test
      void contextLoads() {
      }
    }
    
  9. How Methods are mocked Internally?
    Mockito works by storing and retrieving method invocation details on mocks using method interception
    When we invoke the when() method we are in fact recalling the last registered method call from that context, In our case it is Dep2Method() is saved and returned.The recorded behaviour is played back when our test invokes the mocked method again which triggers the interceptor to recall the return value we provided.In Mockito source code, we see when method does not use the parameter methodCall. But it tries to get the OngoingStubbing from an instance of MockingProgress. This OngoingStubbing is nothing but mocking context above.

     @Test
        void doIt() {
            ClassADep1.Dep2Method();
            Mockito.when("Some Blah Blah").thenReturn("Mocked!");
            assertEquals(objTarget.doIt("Sample"), "Mocked!");
        }
    
  10. What is the difference between @ExtendWith(SpringExtension.class) and @ExtendWith(MockitoExtension.class)?
    If you want to use Spring test framework features in your tests like for example @MockBean, then you have to use @ExtendWith(SpringExtension.class). It replaces the deprecated JUnit4 @RunWith(SpringJUnit4ClassRunner.class)

    When NOT involving Spring – If you just want to involve Mockito and don’t have to involve Spring, for example, when you just want to use the @Mock / @InjectMocks annotations, then you want to use @ExtendWith(MockitoExtension.class), as it doesn’t load in a bunch of unneeded Spring stuff. It replaces the deprecated JUnit4 @RunWith(MockitoJUnitRunner.class).

  11. When to use @Extendwith and @Runwith?
    If you are using Junit version < 5, so you have to use @RunWith(SpringRunner.class) or @RunWith(MockitoJUnitRunner.class) etc.If you are using Junit version = 5, so you have to use @ExtendWith(SpringExtension.class) or @ExtendWith(MockitoExtension.class) Junit4 uses *Runner.class in annotation with @RunWith

    SpringRunner.class and MockitoJunitRunner.class for Junit4
    SpringExtension.class and MockitoExtension.class for Junit5
    
  1. Till now we were sending requests over HTTP. Now lets make HTTP secured using https by installing certificate in our project
  2. To generate certificate we use Key Store Explorer and will generate PKCS#12 Certificate

  3. Generate a public and private key and save the keystore
  4. The Keystore should be copied to the resources folder and the credentials of the keystore should be added to application.properties as below.

    application.properties

    # The format used for the keystore. It could be set to JKS in case it is a JKS file
    server.ssl.key-store-type=PKCS12
    # The path to the keystore containing the certificate
    server.ssl.key-store=classpath:TestCert
    # The password used to generate the certificate
    server.ssl.key-store-password=password
    
  5. Since the certificare generated(keystore file) cannot be cross checked with CA(Certificate Authority) it would display message like one below for the authenticy of certificate, whether it should be accpeted or not. How ever you can continue further by clicking on advanced

Note: While performing CRUD the addition of user is a JSON request which should be carried out only from postman. For this enable interceptor plugin in Chrome so that cookies set in chrome would be available in Postman.

Further Reads:
How SSL Works

The server gives the client a one-time-use number (a nonce) that it combines with the username, realm, password, and URI request. The client runs all of those fields through an MD5 hashing method to produce a hash key.

It sends this hash key to the server along with the username and the realm to attempt to authenticate.
Server-side the same method is used to generate a hash key, only instead of using the password typed into the browser, the server looks up the expected password for the user from its user DB. It looks up the stored password for this username, runs in through the same algorithm, and compares it to what the client sent. If they match then access is granted, otherwise, it can send back a 401 Unauthorized (no login or failed login) or a 403 Forbidden (access denied).

Steps

  1. Client makes request
  2. Client gets back a nonce from the server and a 401 authentication request
  3. Client sends back the following response array
     (username, realm, generate_md5_key(nonce, username, realm, URI, password_given_by_user_to_browser)) 
    
  4. The server takes username and realm (plus it knows the URI the client is requesting) and it looks up the password for that username in DB. Then it goes and does its own version of
     generate_md5_key(nonce, username, realm, URI, password_I_have_for_this_user_in_my_db)
    
  5. It compares the output of generate_md5() that it got with the one the client sent, if they match the client sent the correct password. If they don’t match the password sent was wrong.

It doesn’t require sending the username and password across the wire in plaintext. It is also immune to replay-attacks, as it uses a one-time number from the server.

Lets assume, Alice sent a message and digest pair to Bob. To check the integrity of the message Bob runs the cryptographic hash function on the received message and gets a new digest. Now, Bob will compare the new digest and the digest sent by Alice. If, both are same then Bob is sure that the original message is not changed.

Step by Step Action

  1. client sends a request for an access-protected resource, but an acceptable Authorization header field is not sent
  2. server responds with a “401 Unauthorized” status code and a WWW-Authenticate header field (the digest-challenge)
  3. client sends another request for the same resource but containing a Authorization header field in response to the challenge (the digest-response)
  4. if the authorization is not successful, go to step 2; otherwise the server proceeds as normal.

Streams

  1. Intermediate Operations returns Stream as output. methods like map(), sorted(), distinct() would return stream as output
  2. Terminal Operations returns something other than Stream. Collect() returns collection. Reduce() returns one element

Converting list to a stream and print them

List<Integer> arrNumbers = List.of(10,27,31, 35, 40, 44, 48, 50);
//Convert list to stream
Stream objStream = arrNumbers.stream();

//Static Method Reference
objStream.forEach(System.out::println);

Both Map and Filter returns Streams.
Filter takes predicate as input and adds element to stream based on result of predicate(I.E. True or false).
Map takes function as input and adds transformed object value to stream.
Reduce takes function as input and returns a single value that is computed going over the entire rows in stream.

Filter
Filter is used for filtering the data, it always returns the boolean value. If it returns true, the item is added to list else it is filtered out (ignored)

  1. Filter takes a predicate as argument. Predicate returns boolean. The Output of Filter is a Stream
  2. Predicate object which is technically a function to convert an object to boolean. We pass an object and it will return true or false.
  3. filter() method is lazy like map, it wont be evaluated until you call a reduction method, like collect

Using Filter to filter names in list and print them

import java.util.*;
import java.util.stream.Stream;

List<String> arrNames = List.of("Mugil", "Mani", "Madhu");
arrNames.stream()
        .filter(name -> name.equals("Mugil"))
        .forEach(System.out::println);
List<Integer> arrNumbers = List.of(10,27,31, 35, 40, 44, 48, 50);
List<String> arrSkills = List.of("Spring Boot", "Spring Security", "Java 8", "Microservices", "Spring MVC");

//Printing Even Numbers
System.out.println("------Even Numbers-------");
arrNumbers.stream().filter(FilterEgs::isEven).forEach(System.out::println);

//Printing Even Numbers
System.out.println("------Even Numbers-------");
arrNumbers.stream().filter(no -> no%2==0).forEach(System.out::println);

//Printing Odd Numbers
System.out.println("------Odd Numbers-------");
arrNumbers.stream().filter(no -> no%2==1).forEach(System.out::println);

//Printing Odd Numbers
System.out.println("------Print Spring Skills-------");
arrSkills.stream().filter(skill -> skill.toLowerCase().contains("spring"))
.forEach(System.out::println);

private static boolean isEven(int no){
        return no%2==0;
}       

Output

------Even Numbers-------
10
40
44
48
50
------Odd Numbers-------
27
31
35
------Print Spring  Skills-------
Spring Boot
Spring Security
Spring MVC

Map
By using map, you transform the object values. the map returns the transformed object value as Stream.

  1. Map takes a function as a argument and performs transform action on elements. The Output of Map is a Stream
  2. map() is used to transform one object into another by applying a function.
  3. Stream.map(Function mapper) takes a function as an argument.
  4. mapping function converts one object to the other. Then, the map() function will do the transformation for you. It is also an intermediate Stream operation, which means you can call other Stream methods, like a filter, or collect on this to create a chain of transformations.
List<Integer> arrNum = List.of(5,6,8,57,4,1,26,84,15);
List<String> arrSkills = List.of("Spring Boot", "Spring Security", "Java 8", "Microservices", "Spring MVC");


//Printing Square of Numbers whose value less than 15
System.out.println("------Square of Numbers-------");
arrNum.stream()
      .filter(num -> num<15)   
      .map(num -> num*num)
      .forEach(System.out::println);


//Print Total Characters in String
System.out.println("------Print Total Characters in String-------");
arrSkills.stream()
		.map(skill -> skill + " contains "+ skill.length()+ " Characters")
		.forEach(System.out::println);

Output

------Square of Numbers-------
25
36
64
16
1
------Print Total Characters in String-------
Spring Boot Contains 11 Characters
Spring Security Contains 4 Characters
Java 8 Contains 5 Characters

Reduce

  1. Stream.reduce() takes function as Parameter
  2. Reduction stream operations allow us to produce one single result from a sequence of elements, by repeatedly applying a combining operation to the elements in the sequence.
  3. Stream.reduce() operation Contains Identity, Accumulator and Combiner
  4. Identity – an element that is the initial value of the reduction operation and the default result if the stream is empty
  5. Accumulator – a function that takes two parameters: a partial result of the reduction operation and the next element of the stream
  6. Combiner – a function used to combine the partial result of the reduction operation when the reduction is parallelized or when there’s a mismatch between the types of the accumulator arguments and the types of the accumulator implementation

ReduceEgs.java

import java.util.Arrays;
import java.util.List;

public class ReduceEgs {
    public static void main(String[] args) {
        List<Integer> arrNumbers =  Arrays.asList(1,2,3,4,5,6,7,8,9);

        System.out.println(arrNumbers.stream().reduce(0, ReduceEgs::sumNos));
    }

    public static Integer sumNos(int x, int y){
        System.out.println("a -"+ x + " b - "+  y);

        return x+y;
    }
}

Output

a -0 b - 1
a -1 b - 2
a -3 b - 3
a -6 b - 4
a -10 b - 5
a -15 b - 6
a -21 b - 7
a -28 b - 8
a -36 b - 9
45

In the above example

  1. the Integer value 0 is the identity. It stores the initial value of the reduction operation and also the default result when the stream of Integer values is empty.
  2. the lambda expression x+y represented by function sumNos is the accumulator since it takes the partial sum of Integer values and the next element in the stream.
  3. When a stream executes in parallel, the Java runtime splits the stream into multiple substreams. In such cases, we need to use a function to combine the results of the substreams into a single one. This is the role of the combiner

Role of combiner would be visibile only in parallelStream with output as below. It would be hard to interpret the output response in console.

ReduceEgs.java

.
.
 System.out.println(arrNumbers.parallelStream().reduce(0, ReduceEgs::sumNos));
.
.

Output

a -0 b - 6
a -0 b - 5
a -5 b - 6
a -0 b - 1
a -0 b - 7
a -0 b - 9
a -0 b - 2
a -1 b - 2
a -0 b - 3
a -0 b - 8
a -8 b - 9
a -0 b - 4
a -3 b - 4
a -3 b - 7
a -7 b - 17
a -11 b - 24
a -10 b - 35
45

Alternate ways to Sum Nos

public static void main(String[] args) {
 List<Integer> arrNumbers =  Arrays.asList(1,2,3,4,5,6,7,8,9);

 Integer sum = arrNumbers.stream().reduce(0, Integer::sum);
 Integer sum2 = arrNumbers.stream().reduce(0, (x,y)->x+y);

 System.out.println(sum);
 System.out.println(sum2);
}

Once you get the values in stream then it should be typecast to its original type before calling its methods as values are stored as Objects in stream

Below you could see the name is typecast to String before calling lowerCase method

List<String> arrNames = Arrays.asList("Mugil", "Mani", "Vinu");
Stream arrNameStream = arrNames.stream();
arrNameStream.forEach(name -> System.out.println(((String)name).toLowerCase()));
  1. What is the Difference between Map and Filter?
    Both perform intermediate Operations and returns stream as output.By using map, you transform the object values.
    Map returns a stream consisting of the results of applying the given function to the elements of this stream. In a simple sentence, the map returns the transformed object value.
    Filter is used for filtering the data, it always returns the boolean value. If it returns true, the item is added to list else it is filtered out (ignored)
  2. Find the Sum of Nos in List?
    public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3,4,5,6,7,8,9);
    
            Integer sum = arrNumbers.stream().reduce(0, Integer::sum);
            Integer sum2 = arrNumbers.stream().reduce(0, (x,y)->x+y);
            Integer sum3 = arrNumbers.stream().reduce(0, ReduceEgs::sumNos);
        }
    
        public static Integer sumNos(int x, int y){
            System.out.println("a -"+ x + " b - "+  y);
            return x+y;
        }
    }
    
  3. Find the Min and Max in Nos List?
     public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3,4,5,-6,7,-8,9);
    
            arrNumbers =  Arrays.asList(-5,-6,-8);
            Integer Max = arrNumbers.stream().reduce(0, (x,y)->x>y?x:y);
            System.out.println("Output 1 ="+ Max);
    
            //Its good to start with Identity as Negative Value in order to address negative Integer Comparison
            //If you start with 0 then 0 would be returned like above
            Integer Max2 = arrNumbers.stream().reduce(Integer.MIN_VALUE, (x,y)->x>y?x:y);
            System.out.println("Output 2 ="+ Max2);
    
            arrNumbers =  Arrays.asList(5,6,8);
    
            Integer Min = arrNumbers.stream().reduce(0, (x,y)->x<y?x:y);
            System.out.println("Output 3 ="+ Min);
    
            //Its good to start with Identity as Max Positive Value in order to address Integer Comparison
            Integer Min2 = arrNumbers.stream().reduce(Integer.MAX_VALUE, (x,y)->x<y?x:y);
            System.out.println("Output 4 ="+ Min2);
        }
    

    Output

    Output 1 =0
    Output 2 =-5
    Output 3 =0
    Output 4 =5
    
  4. Find the Sum of Square of Nos in List?
    public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3);
    
            Integer squareSum = arrNumbers.stream().map(x->x*x).reduce(0, Integer::sum);
            System.out.println("Output 1 ="+ squareSum);
        }
    }
    

    Output

    Output 1 =14
    
  5. Find the Sum of Odd Nos in List?
    public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3);
    
            Integer squareSum = arrNumbers.stream().filter(x-> x%2==1).reduce(0, Integer::sum);
            System.out.println("Output 1 ="+ squareSum);
        }
    }
    

    Output

    Output 1 =4
    
  6. Distinct elements from List?
    public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers =  Arrays.asList(1,2,3,1,4,2);
    
            System.out.println("Output 1");
            arrNumbers.stream().distinct().forEach(System.out::println);
        }
    }
    
    

    Output

    Output 1
    1
    2
    3
    4
    
  7. Sort Elements in List?
     public static void main(String[] args) {
       List<Integer> arrNumbers =  Arrays.asList(1,2,3,1,4,2);
    
       System.out.println("Output 2");
       arrNumbers.stream().sorted().forEach(System.out::println);
    }
    

    Output

    Output 2
    1
    1
    2
    2
    3
    4
    
  8. Sorting based on Natural Order, Reverse Order and Length of String?
    public static void main(String[] args) {
            List<String> arrSkills = Arrays.asList("Java", "Python",  "Ant");
    
            System.out.println("Sorting in Natural Order");
            arrSkills.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
    
            System.out.println("Sorting in Reverse Order");
            arrSkills.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println);
    
            System.out.println("Sorting based on Length");
            arrSkills.stream().sorted(Comparator.comparing(str -> str.length())).forEach(System.out::println);
    
        }
    

    Output

    Sorting in Natural Order
    Ant
    Java
    Python
    Sorting in Reverse Order
    Python
    Java
    Ant
    Sorting based on Length
    Ant
    Java
    Python
    
  9. Collect the Elements in the List – Collect Even Nos and Length of String ?
        public static void main(String[] args) {
            List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);
            List<String> arrNames = Arrays.asList("Java", "Oracle", "Maven", "Ant");
    
            System.out.println("Collect Even Nos in list");
            List<Integer> arrEvenNumbers = arrNumbers.stream().filter(x->x%2==0).collect(Collectors.toList());
            arrEvenNumbers.stream().forEach(System.out::println);
    
            System.out.println("Length of Elements");
            List<Integer> arrEvenNumbers2 = arrNames.stream().map(str->str.length()).collect(Collectors.toList());
            arrEvenNumbers2.stream().forEach(System.out::println);
        }
    

    Output

    Collect Even Nos in list
    2
    4
    6
    Length of Elements
    4
    6
    5
    3
    
  10. Find the Sum of Nos in List?
    
    
  11. Find the Sum of Nos in List?
    
    
  12. Find the Sum of Nos in List?
    
    

When to use Lambda Expressions
If you have a functional Interface which has only one Abstract Method then we can use Lambda Expression at the Class which implements the functional interface
and provides the method definition for the abstract method in Functional Interface.

  1. We have a Function Interface Printable with abstract method print
  2. Two classes implements functional Interface – DailyReport and MothlyReport. They provide the method definition for Print() method in Printable Interface
  3. GenerateReport class has a downloadReport method which takes the Printable Type as argument
  4. DownloadReport Class we use the Printable reference and call the exact implementation by passing as argument

Printable.java

@FunctionalInterface
public interface Printable {
    public void print();
}

DailyReport.java

public class DailyReport implements Printable {
    @Override
    public void print() {
        System.out.println("Printing Report in Daily Format");
    }
}

MonthlyReport.java

public class MonthlyReport implements Printable {
    @Override
    public void print() {
        System.out.println("Printing Report in Monthly Format");
    }
}

GenerateReport.java

public class GenerateReport {
    public static void downloadReport(Printable printable){
        printable.print();
    }
}

Without Lambda Expression
DownloadReport.java

public class DownloadReport {
    public static void main(String[] args) {
        GenerateReport objGenReport = new GenerateReport();
        Printable objDailyReport = new DailyReport();
        Printable objMonthlyReport = new MonthlyReport();

        objGenReport.downloadReport(objDailyReport);
        objGenReport.downloadReport(objMonthlyReport);
    }
}

Output

Printing Report in Daily Format
Printing Report in Monthly Format

With Lambda Expression
Using Lambda expression we can pass method definition directly as parameter instead of using a class to extend interface and writing a method definition for the interface method
DownloadReport.java

public class DownloadReport {
    public static void main(String[] args) {
        GenerateReport objGenReport = new GenerateReport();
        objGenReport.downloadReport(() -> System.out.println("Printing Report in Daily Format using Lambda Expr"));

        Printable mnthlyReport = () -> System.out.println("Printing Report in Monthly Format using Lambda Expr");
        objGenReport.downloadReport(mnthlyReport);     
    }
}

Output

Printing Report in Daily Format using Lambda Expr
Printing Report in Monthly Format using Lambda Expr
With Lambda Expression Without Lambda Expression
We pass the method definition as Argument
  1. Method Definition in MonthlyReport.java
  2. Method Definition in DailyReport.java
  3. Object creation for DailyReport and MonthlyReport

Let’s take the below Example where we find the square of even numbers

ReduceEgs.java

public class ReduceEgs {
    public static void main(String[] args) {
        List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);

        arrNumbers.stream()
                  .filter(x-> x%2==0)
                  .map(x->x*x)
                  .forEach(System.out::println);
    }
}

In the above code we have

  1. Filter which takes Predicate as Param
  2. Map which takes Function as Param
  3. Sysout which takes Consumer as Param

The filter, Consumer and sysout have been expanded from the above code as below.

public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);
    
            arrNumbers.stream()
                      .filter(getIntegerPredicate())
                      .map(getFunction())
                      .forEach(getPrintln());
        }
    
        private static Consumer<Integer> getPrintln() {
            return System.out::println;
        }
    
        private static Function<Integer, Integer> getFunction() {
            return x -> x * x;
        }
    
        private static Predicate<Integer> getIntegerPredicate() {
            return x -> x % 2 == 0;
        }
}

The Above code is refactored as below

public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);

            Function<Integer, Integer> getSquare = x -> x * x;
            Predicate<Integer>         getEvenNo = x -> x % 2 == 0;
            Consumer<Integer>          showNos   = System.out::println;

            arrNumbers.stream()
                      .filter(getEvenNo)
                      .map(getSquare)
                      .forEach(showNos);
        }
}

What the java compiler does internally changes the above code as below. It created an anonymous inner class for implementing the methods in the interface.

  1. Function has apply method which should be implemented
  2. Consumer has accept method which should be implemented
  3. Predicate has test method which should be implemented
public class ReduceEgs {
        public static void main(String[] args) {
            List<Integer> arrNumbers = Arrays.asList(1,2,4,5,7,6);

            Function<Integer, Integer> getSquare = new Function<Integer, Integer>() {
                @Override
                public Integer apply(Integer integer) {
                    return integer * integer;
                }
            };

            Predicate<Integer> getEvenNo =  new Predicate<Integer>() {
                @Override
                public boolean test(Integer integer) {
                    return  integer % 2 == 0;
                }
            };

            Consumer<Integer> showNos = new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) {
                    System.out.println(integer);
                }
            };

            arrNumbers.stream()
                      .filter(getEvenNo)
                      .map(getSquare)
                      .forEach(showNos);
        }
}

The output of all the above code is one and same as below

4
16
36

We have a Employee Object with following fields – empId,salary,empAge,totalExp,empName,location,pincode. Now we are going to do following operations in the List containing Employee Object.

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class MatchEgs {
    public static void main(String[] args) {
        Employee objEmp1 = new Employee(101, "Mugil", 30, "Chennai", "600018", 5, 5000);
        Employee objEmp2 = new Employee(102, "Mani", 33, "Chennai", "600028", 4, 6000);
        Employee objEmp3 = new Employee(103, "Madhu", 32, "Chennai", "600054", 6, 10000);
        Employee objEmp4 = new Employee(104, "Vinu", 29, "Bangalore", "500234", 5, 15000);
        Employee objEmp5 = new Employee(105, "Srini", 40, "Delhi", "622142", 10, 7000);
        Employee objEmp6 = new Employee(106, "Dimple", 35, "Delhi", "622142", 5, 8000);

        List<Employee> arrEmployee = Arrays.asList(objEmp1, objEmp2, objEmp3, objEmp4, objEmp5, objEmp6);
 }
}

Check if people match the criteria by passing predicate to anyMatch, noneMatch and allMatch

        Predicate<Employee> empAgeGreaterThan35 = employee -> employee.getEmpAge() > 35;
        Predicate<Employee> empAgeGreaterThan30 = employee -> employee.getEmpAge() > 30;
        Predicate<Employee> empStayingInDelhi   = employee -> employee.getLocation().equals("Delhi");

        //Check if Some Employee Age is Greater 30
        System.out.println(arrEmployee.stream().anyMatch(empAgeGreaterThan35));

        //Check if all Employee Age is above 30
        System.out.println(arrEmployee.stream().allMatch(empAgeGreaterThan30));

        //Check if any Employee Location in Delhi
        System.out.println(arrEmployee.stream().noneMatch(empStayingInDelhi));

Sort Employee by Name passing Comparator using Sorted and reversed. Sort based on more than
one field using thenComparing

        Comparator<Employee> sortByName = Comparator.comparing(Employee::getEmpName);
        List<Employee> arrSortedByName = arrEmployee.stream()
                                                    .sorted(sortByName)
                                                    .collect(Collectors.toList());
        System.out.println("----------------Sorted By Name----------------");
        arrSortedByName.forEach(employee -> System.out.println(employee));

        Comparator<Employee> sortByNameReversed = Comparator.comparing(Employee::getEmpName).reversed();
        List<Employee> arrSortedByNameRev = arrEmployee.stream()
                                                       .sorted(sortByNameReversed)
                                                       .collect(Collectors.toList());
        System.out.println("----------------Sorted By Name Reversed----------------");
        arrSortedByNameRev.forEach(employee -> System.out.println(employee));

        Comparator<Employee> sortByNameanAge = Comparator.comparing(Employee::getEmpName).thenComparing(Employee::getEmpAge);
        List<Employee> arrSortedByNameAge = arrEmployee.stream()
                                                       .sorted(sortByNameanAge)
                                                       .collect(Collectors.toList());
        System.out.println("----------------Sorted By Name and Age----------------");
        arrSortedByNameAge.forEach(employee -> System.out.println(employee));

Limit and Skip records using limit and skip

        System.out.println("----------------Limit Records to 3 ----------------");
        List<Employee> arrEmp3 =  arrEmployee.stream()
                                             .limit(3)
                                             .collect(Collectors.toList());
        arrEmp3.forEach(employee -> System.out.println(employee));

        System.out.println("----------------Skip First 3 Records ----------------");
        List<Employee> arrEmp4 =  arrEmployee.stream()
                                             .skip(3)
                                             .collect(Collectors.toList());
        arrEmp4.forEach(employee -> System.out.println(employee));

doWhile when condition is true using takeWhile and vice-versa(Until condition is met) using dropWhile

        System.out.println("----------------Print Records until Chennai - Keeps printing until condition is True----------------");
        List<Employee> arrEmp5 =  arrEmployee.stream()
                                             .takeWhile(employee -> employee.getLocation().equals("Chennai"))
                                             .collect(Collectors.toList());
        arrEmp5.forEach(employee -> System.out.println(employee));

        System.out.println("----------------Print Records until Chennai - Stops printing when condition is Met ----------------");
        List<Employee> arrEmp6 =  arrEmployee.stream()
                                             .dropWhile(employee -> employee.getLocation().equals("Bangalore"))
                                             .collect(Collectors.toList());
        arrEmp5.forEach(employee -> System.out.println(employee));

Get Minimum and Maximum age of employee using min and max. The List should be sorted first using comparator. Similarly get Employee with max experience in a particular location using predicate, comparator and max.

        System.out.println("----------------Record with Min and Max Value ----------------");
        Comparator<Employee> sortByAge = Comparator.comparing(Employee::getEmpAge);
        List<Employee> arrEmp7 =  arrEmployee.stream()
                                             .min(sortByAge)
                                             .stream()
                                             .collect(Collectors.toList());
        List<Employee> arrEmp8 =  arrEmployee.stream()
                                             .max(sortByAge)
                                             .stream()
                                             .collect(Collectors.toList());
        arrEmp7.forEach(employee -> System.out.println(employee));
        arrEmp8.forEach(employee -> System.out.println(employee));

        System.out.println("----------------Get Employee with Max Exp in Chennai ----------------");
        Predicate<Employee> filterEmpInChennai = employee -> employee.getLocation().equals("Chennai");
        Comparator<Employee> sortByExp = Comparator.comparing(Employee::getTotalExp);
        List<Employee> arrEmp11 =  arrEmployee.stream()
                                              .filter(filterEmpInChennai)
                                              .max(sortByExp)
                                              .stream()
                                              .collect(Collectors.toList());
        arrEmp11.forEach(employee -> System.out.println(employee));

FindFirst Employee who matches criteria and FindAny who matches criteria. Both takes predicate as input.

findAny – Returns an Optional describing some element of the stream, or an empty Optional if the stream is empty.it is free to select any element in the stream. This is to allow for maximal performance in parallel operations;

        System.out.println("----------------Find First  ----------------");
        Predicate<Employee> empStayingInChennai = employee -> employee.getLocation().equals("Chennai");
        List<Employee> arrEmp9 =  arrEmployee.stream()
                                             .filter(empStayingInChennai)
                                             .findFirst()
                                             .stream()
                                             .collect(Collectors.toList());

        arrEmp9.forEach(employee -> System.out.println(employee));

        System.out.println("----------------Find Any  ----------------");
        List<Employee> arrEmp10 =  arrEmployee.stream()
                                              .filter(empAgeGreaterThan30)
                                              .findAny()
                                              .stream()
                                              .collect(Collectors.toList());
        arrEmp10.forEach(employee -> System.out.println(employee));

Get the Sum of salary of Employees in Location(Chennai) using sum

         System.out.println("----------------Sum - Get Sum of Salary in Chennai  ----------------");
        //Method 1
        Integer empTotalSalaryInChennai1 = arrEmployee.stream()
                                                      .filter(empStayingInChennai)
                                                      .map(employee -> employee.getSalary())
                                                      .collect(Collectors.toList())
                                                      .stream()
                                                      .reduce(0, Integer::sum);
        System.out.println("Sum of Empl Salary - Chennai " + empTotalSalaryInChennai1);

        //Method 2
        Integer empTotalSalaryInChennai2 = arrEmployee.stream()
                                                      .filter(empStayingInChennai)
                                                      .mapToInt(Employee::getSalary).sum();
        System.out.println("Sum of Empl Salary - Chennai " + empTotalSalaryInChennai2);

        //Method 3
        Integer empTotalSalaryInChennai3 = arrEmployee.stream() 
                                                      .filter(empStayingInChennai)
                                                      .map(employee -> employee.getSalary())
                                                      .collect(Collectors.summingInt(Integer::intValue));
        System.out.println("Sum of Empl Salary - Chennai " + empTotalSalaryInChennai3);

Get Max and Min salary of employee

  Comparator<Employee> cmpSalComp = (Employee objEmpp1, Employee objEmpp2) -> Double.compare(objEmpp1.Salary,objEmpp2.Salary);

  Employee empMaxObj = arrEmpl.stream()
                                   .max(cmpSalComp)
                                   .get();
                                   
  Employee empMinObj =   arrEmpl.stream()
                                 .min(cmpSalComp)
                                 .get();

Get Average salary of employee grouped by Location

        System.out.println("----------------Average - Grouped by Location  ----------------");
        Map<String, Double> arrAvgSalByLoc =  arrEmpl.stream()
                                                     .collect(Collectors.groupingBy(Employee::getLocation, Collectors.averagingDouble(Employee::getSalary)));
                                         
        System.out.print(arrAvgSalByLoc);

Get Average salary of employee in location using average

        System.out.println("----------------Average - Get Average of Salary in Chennai  ----------------");
        OptionalDouble empAvgSalaryInChennai = arrEmployee.stream()
                                                          .filter(empStayingInChennai)
                                                          .mapToInt(Employee::getSalary)
                                                          .average();
        System.out.println("Average Salary of Employee - Chennai " + empAvgSalaryInChennai);

Get List of Employees in a location using groupingBy. The Return type is hashmap with location as key and List of employees in value

        System.out.println("----------------Group By -  Get Employees grouped by Location  ----------------");
        Map<String, List<Employee>> hmEmp13 = arrEmployee.stream()
                                                         .collect(Collectors.groupingBy(Employee::getLocation));
        hmEmp13.forEach((s, employees) -> {
            System.out.println("Employees from "+ s);
            employees.forEach(employee -> System.out.println(employee));
        });

Get Employee grouped by Location and getting Maximum Salary groupingBy and maxBy

        System.out.println("----------------Group By -  Get Employees with Max Salary in Location  ----------------");
        Comparator<Employee> cmprSalary = Comparator.comparing(Employee::getSalary);
        Map<String, Optional<Employee>> hmEmp14 = arrEmployee.stream()
                                                             .collect(Collectors.groupingBy(Employee::getLocation, Collectors.maxBy(cmprSalary)));

        hmEmp14.forEach((s, employee) -> {
            System.out.print("Employee Location is "+ s +  " and salary is ");
            employee.ifPresent(emp -> System.out.println(emp.getSalary()));
        });

Get Employee Name using mapping grouped by Location using groupingBy

        System.out.println("---------------- Employee at Location -  Using Collectors.mapping  ----------------");
        Map<String, List<String>> hmEmp16 = arrEmployee.stream()
                                                       .collect(Collectors.groupingBy(Employee::getLocation, Collectors.mapping(Employee::getEmpName, Collectors.toList())));

        hmEmp16.forEach((s, employee) -> {
            System.out.println("Employee Name is " + employee + " and Location is "+ s );

        });

Output

true
false
false
----------------Sorted By Name----------------
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
----------------Sorted By Name Reversed----------------
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
----------------Sorted By Name and Age----------------
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
----------------Limit Records to 3 ----------------
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
----------------Skip First 3 Records ----------------
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
----------------Print Records until Chennai - Keeps printing until condition is True----------------
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
----------------Print Records until Chennai - Stops printing when condition is Met ----------------
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
----------------Record with Min and Max Value ----------------
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
----------------Get Employee with Max Exp in Chennai ----------------
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
----------------Find First  ----------------
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
----------------Find Any  ----------------
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
----------------Sum - Get Sum of Salary in Chennai  ----------------
Sum of Empl Salary - Chennai 21000
Sum of Empl Salary - Chennai 21000
Sum of Empl Salary - Chennai 21000
----------------Average - Get Average of Salary in Chennai  ----------------
Average Salary of Employee - Chennai OptionalDouble[7000.0]
----------------Group By -  Get Employees grouped by Location  ----------------
Employees from Delhi
Employee{empId=105, empName='Srini', empAge=40, totalExp=10, location='Delhi', pincode='622142', salary=7000}
Employee{empId=106, empName='Dimple', empAge=35, totalExp=5, location='Delhi', pincode='622142', salary=8000}
Employees from Chennai
Employee{empId=101, empName='Mugil', empAge=30, totalExp=5, location='Chennai', pincode='600018', salary=5000}
Employee{empId=102, empName='Mani', empAge=33, totalExp=4, location='Chennai', pincode='600028', salary=6000}
Employee{empId=103, empName='Madhu', empAge=32, totalExp=6, location='Chennai', pincode='600054', salary=10000}
Employees from Bangalore
Employee{empId=104, empName='Vinu', empAge=29, totalExp=5, location='Bangalore', pincode='500234', salary=15000}
----------------Group By -  Get Employees with Max Salary in Location  ----------------
Employee Location is Delhi and salary is 8000
Employee Location is Chennai and salary is 10000
Employee Location is Bangalore and salary is 15000
---------------- Employee at Location -  Get Employee Object grouped by Location  ----------------
Employee Location is Delhi
Srini
Dimple
Employee Location is Chennai
Mugil
Mani
Madhu
Employee Location is Bangalore
Vinu
---------------- Employee at Location -  Using Collectors.mapping  ----------------
Employee Name is [Srini, Dimple] and Location is Delhi
Employee Name is [Mugil, Mani, Madhu] and Location is Chennai
Employee Name is [Vinu] and Location is Bangalore