In this article, we will discuss Stream’s reduce() method in detail with examples
1. Stream reduce() method :
- This Stream method is a terminal operation which performs reduction on the given stream and returns a reduced (or single) value
 - There are 3 variants of reduce() method
 - Method signature 1 :- Optional<T> reduce(BinaryOperator<T> accumulator)
 - Method signature 2 :- T reduce(T identity, BinaryOperator<T> accumulator)
 - Method signature 3 :- <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
 - reduce() method helps to derive sum of the Stream, searching max/mix element amongst Stream elements or finding average and performing String concatenation
 - We can utilize this method for Map and reduce functionality
 
2. Stream reduce() method with accumulator :
- This is the first variant of the reduce() method which takes only one argument BinaryOperator as accumulartor
 - This accumulator perform reduction on the given Stream elements using Lambda expression or Method Reference
 - Method signature :- Optional<T> reduce(BinaryOperator<T> accumulator)
 - Throws NullPointerException if the result of the reduction is null
 
2.1 Summing Stream elements
- In this Stream reduce() example, we are going to summate all elements present in the Stream using Lambda Expression as well as Method Reference
 - For lambda expression – reduce((m,n) -> m+n)
 - For method reference – reduce(Integer::sum)
 - Both these approaches returns OptionalInt
 - We can easily check whether value is present using ifPresent() method and print to console, if present
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
import java.util.OptionalInt;
public class StreamReduceForSumUsingReduceAccumulator {
	public static void main(String[] args) {
		// primitive int[] array
		int[] intArray = {
				17, 23, 29, 31, 37, 41, 43, 47, 53, 58
		};
		// 1.1 reduce() - sum using Lambda Expression
		OptionalInt optionalIntResult1 = Arrays
				.stream(intArray)
				.reduce((m,n) -> m+n); // summation
		// 1.2 get value, if present
		optionalIntResult1.ifPresent(
				sum -> System.out.println("Stream.reduce() - "
						+ "Sum using Lambda Expresion = " 
						+ sum));
		// 2.1 reduce() - sum using Method Reference
		OptionalInt optionalIntResult2 = Arrays
				.stream(intArray)
				.reduce(Integer::sum); // summation
		// 2.2 get value, if present
		optionalIntResult2.ifPresent(
				sum -> System.out.println("Stream.reduce() - "
						+ "Sum using Method Reference = " 
						+ sum));
	}
}
Output:
Stream.reduce() - Sum using Lambda Expresion = 379
Stream.reduce() - Sum using Method Reference = 379
2.2 Searching maximum/minimum element from Stream
- In this Stream reduce() example, we are going to find greatest and smallest element from Stream using Lambda Expression as well as Method Reference
 - For finding greatest/maximum element from Stream, use reduce((x, y) -> (x > y ? x : y)) for lambda expression and reduce(Integer::max) for method reference
 - For finding smallest/minimum element from Stream, use reduce((x, y) -> (x < y ? x : y)) for lambda expression and reduce(Integer::min) for method reference
 - All the above approaches returns OptionalInt which has a useful called ifPresent() to check whether value is present and we can print to console, if present
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
public class FindMinAndMaxUsingReduceAccumulator {
	public static void main(String[] args) {
		// primitive int[] array
		int[] intArray = {
				17, 23, 29, 31, 37, 41, 43, 47, 53, 59
		};
		// 1.1 reduce() - find Maximum element using Lambda Expression
		Arrays
		.stream(intArray)
		.reduce((x, y) -> (x > y ? x : y)) // find greatest
		.ifPresent(
				max -> System.out.println("Stream.reduce() - "
						+ "Maximum element using Lambda Expresion = " 
						+ max));
		// 1.2 reduce() - find Maximum element using Method Reference
		Arrays
		.stream(intArray)
		.reduce(Integer::max) // find greatest
		.ifPresent(
				max -> System.out.println("\nStream.reduce() - "
						+ "Maximum element using Method Reference = " 
						+ max));
		// 2.1 reduce() - find Minimum using Lambda Expression
		Arrays
		.stream(intArray)
		.reduce((x, y) -> (x < y ? x : y)) // find smallest
		.ifPresent(
				min -> System.out.println("\n\nStream.reduce() - "
						+ "Minimum element using Lambda Expresion = " 
						+ min));
		// 2.2 reduce() - find Minimum using Method Reference
		Arrays
		.stream(intArray)
		.reduce(Integer::min) // find smallest
		.ifPresent(
				min -> System.out.println("\nStream.reduce() - "
						+ "Minimum element using Method Reference = " 
						+ min));
	}
}
Output:
Stream.reduce() - Maximum element using Lambda Expresion = 59
Stream.reduce() - Maximum element using Method Reference = 59
Stream.reduce() - Minimum element using Lambda Expresion = 17
Stream.reduce() - Minimum element using Method Reference = 17
2.3 Finding average from Stream elements
- In this Stream reduce() example, we are going to find average from Stream elements
 - To get average, we have to find summation of all elements using either Lambda Expression or Method Reference
 - Then we have to divide this sum by number of elements present in the Stream which will return integer part discarding fractional part
 - The above approach isn’t the optimal way, as it discards fractional portion thereby not providing accurate result
 - Therefore, we can use average() method of IntStream to get average as double value which is the most accurate one
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
public class FindAverageUsingReduceAccumulator {
	public static void main(String[] args) {
		// int[] array
		int[] intArray = {
				17, 23, 29, 31, 37, 41, 43, 47, 53
		};
		// 1. Stream.reduce() - average using Lambda Expression
		int average1 = Arrays
				.stream(intArray)
				.reduce((m,n) -> m+n)
				.orElse(0) / intArray.length;
		System.out.println("Stream.reduce() - "
				+ "Average using Lambda Expression = " 
				+ average1);
		// 2. Stream.reduce() - average using Method Reference
		int average2 = Arrays
				.stream(intArray)
				.reduce(Integer::sum)
				.orElse(0) / intArray.length;
		System.out.println("\nStream.reduce() - "
				+ "Average using Method Reference = " 
				+ average2);
		// 3. using average() of IntStream
		double average3 = Arrays
				.stream(intArray)
				.average()
				.getAsDouble();
		System.out.println("\nUsing average() of IntStream = " 
				+ average3);
	}
}
Output:
Stream.reduce() - Average using Lambda Expression = 35
Stream.reduce() - Average using Method Reference = 35
Using average() of IntStream = 35.666666666666664
2.4 String Concatenation
- In this Stream reduce() example, we are going to concatenate all Strings present in the Stream using Lambda Expression as well as Method Reference
 - For lambda expression use reduce((str1, str2) -> str1 + ” ” + str2) – this approach helps us to provide delimiter in between 2 String values while concatenating
 - For method reference use reduce(String::concat) – this approach just helps us to concatenate all String together
 - Both these approaches returns Optional<String>
 - We can easily check whether value is present using ifPresent() method and print to console, if present
 - There are 2 more use-cases, where in 1st use-case we are converting String into upper case and then concatenating with space delimiter and in the 2nd use-case we are concatenating with pipe (|) as delimiter between 2 String values
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
import java.util.Optional;
public class StringConcatenationUsingReduceAccumulator {
	public static void main(String[] args) {
		// String[] array
		String[] strArray = {
				"India",
				"is",
				"a",
				"Beautiful",
				"country"
		};
		// 1.1 Stream.reduce() - string concatenation using lambda
		Optional<String> resultLEx = Arrays
				.stream(strArray)
				.reduce((str1, str2) -> str1 + " " + str2);
		// print result, if present
		resultLEx.ifPresent(System.out::println);
		// 1.2 Stream.reduce() - string concatenation using method ref
		Optional<String> resultMRef = Arrays
				.stream(strArray)
				.reduce(String::concat);
		// print result, if present
		resultMRef.ifPresent(System.out::println);
		// 1.3 upper case and string concat
		Arrays
		.stream(strArray)
		.reduce((str1, str2) -> str1.toUpperCase() + " " + str2.toUpperCase())
		.ifPresent(System.out::println); // print to console
		// 1.4 String concatenation with pipe | separator
		Arrays
		.stream(strArray)
		.reduce((str1, str2) -> str1 + "|" + str2)
		.ifPresent(System.out::println); // print to console
	}
}
Output:
India is a Beautiful country
IndiaisaBeautifulcountry
INDIA IS A BEAUTIFUL COUNTRY
India|is|a|Beautiful|country
2.5 Throws NullPointerException when reduction is null
- Stream’s reduce() method with one argument throws NullPointerException when result of reduction is null
 - Note: there is no possibility to provide initial value with this variant, therefore a better variant with initial/default value is discussed in the next section and this helps us to avoid NPE
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
public class ExceptionInReduceAccumulator {
	public static void main(String[] args) {
		// String[] array
		String[] strArray =  {null};
		// string concatenate and print
		Arrays
		.stream(strArray)
		.reduce((str1, str2) -> str1 + " " + str2) // throws NPE
		.ifPresent(System.out::println);
	}
}
Output:
Exception in thread "main" java.lang.NullPointerException
	at java.util.Objects.requireNonNull(Objects.java:203)
	at java.util.Optional.<init>(Optional.java:96)
	at java.util.Optional.of(Optional.java:108)
	at java.util.stream.ReduceOps$2ReducingSink.get(ReduceOps.java:129)
	at java.util.stream.ReduceOps$2ReducingSink.get(ReduceOps.java:107)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:479)
	at net.bench.resources.stream.reduce.example.ExceptionInAccumulator
.main(ExceptionInAccumulator.java:15)
3. Stream reduce() method with Identity and accumulator :
- This is the second variant of the reduce() method which takes two arguments 
1. Identity as initial/default value
2. BinaryOperator as accumulartor - Identity value will be used as a starting value while doing sum/multiply/subtraction/division operations
 - And the same Identity value will be used as a default value when Stream is empty
 - Note: by providing initial/default/starting value to the accumulator makes sure that reduce() won’t throw any exception like NullPointerException as in the case of 1st variant
 - Like 1st variant, this accumulator also perform reduction on the given Stream elements using Lambda expression or Method Reference
 - Method signature :- T reduce(T identity, BinaryOperator<T> accumulator)
 
3.1 Math operation with Stream elements providing initial value
- We are going to perform Math operations like sum, subtract, multiply and divide using reduce() method by providing initial/start value
 - We can use either Lambda Expression or Method Reference for these Math operations
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
public class MathOperationUsingReduceAccumulator {
	public static void main(String[] args) {
		// primitive int[] array
		int[] intArray = {
				17, 23, 29, 31, 37, 41, 43, 47, 53, 58
		};
		// 1.1 reduce() - sum using Method Reference
		int sum = Arrays
				.stream(intArray)
				.reduce(0, Integer::sum); // initial value 0
		System.out.println("Stream.reduce() - "
				+ "Sum with initial value 0 = " 
				+ sum);
		// 1.2 reduce() - subtract using Lambda Expression
		int subtract = Arrays
				.stream(intArray)
				.reduce(0, (m, n) -> m - n); // initial value 0
		System.out.println("Stream.reduce() - "
				+ "Subtract with initial value 0 = " 
				+ subtract);
		// 1.3 reduce() - multiply using Method Reference
		int multiply = Arrays
				.stream(intArray) // initial value 0
				.reduce(0, (m, n) -> m * n); // 0 multiply by whatever is 0
		System.out.println("Stream.reduce() - "
				+ "Multiply with initial value 0 = " 
				+ multiply);
		// 1.4 reduce() - division using Lambda Expression
		int division = Arrays
				.stream(intArray) // initial value 0
				.reduce(0, (m, n) -> m / n); // 0 divide by whatever is 0
		System.out.println("Stream.reduce() - "
				+ "Division with initial value 0 = " 
				+ division);
	}
}
Output:
Stream.reduce() - Sum with initial value 0 = 379
Stream.reduce() - Subtract with initial value 0 = -379
Stream.reduce() - Multiply with initial value 0 = 0
Stream.reduce() - Division with initial value 0 = 0
3.2 String concatenation of Stream elements providing default value
- In this example, we are going to perform String concatenation operations using reduce() method by providing default value
 - We can use either Lambda Expression or Method Reference
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
public class StringOperationUsingReduceAccumulator {
	public static void main(String[] args) {
		// initial value
		String defaultValue = null;
		// String[] array
		String[] strArray = {
				"is",
				"a",
				"Beautiful",
				"destination"
		};
		// 1.1 default value assigned
		defaultValue = "India";
		// 1.2 Stream.reduce() - string concatenation using lambda
		String strConcatIndia = Arrays
				.stream(strArray)
				.reduce(defaultValue, (str1, str2) -> str1 + " " + str2);
		System.out.println("String Concatention 1 = " + strConcatIndia);
		// 2.1 default value assigned
		defaultValue = "Dubai";
		// 2.2 Stream.reduce() - string concatenation using lambda
		String strConcatDubai = Arrays
				.stream(strArray)
				.reduce(defaultValue, (str1, str2) -> str1 + " " + str2);
		System.out.println("String Concatention 2 = " + strConcatDubai);
	}
}
Output:
String Concatention 1 = India is a Beautiful destination
String Concatention 2 = Dubai is a Beautiful destination
3.3 Returns default value when Stream is empty
- When Stream is empty, then default/initial value is used for that particular operation using 2nd variant of reduce() method
 - In the below illustration, we have used 3 different examples to show reduction when Stream is empty
 - First, we are doing summation when Stream is empty and starting value is 1, so final sum is 1
 - Second, we are doing multiplication when Stream is empty providing initial value 1, so final result of multiplication is 1
 - Third, we are doing String concatenation when Stream is empty with default value ‘Australia’ and final string concatenation result is “Australia”
 - Note: nowhere we have faced any exception when we have provided default/initial/starting value in the reduce() method
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
public class StreamIsEmpty {
	public static void main(String[] args) {
		// 1. empty primitive int array
		int[] intArrayEmpty = {};
		// 1.1 reduce() - sum using Method Reference
		int sum = Arrays
				.stream(intArrayEmpty)
				.reduce(1, Integer::sum); // initial value 1
		// 1.2 print to console
		System.out.println("Stream.reduce() - Sum on empty "
				+ "array with initial value 1 = " 
				+ sum);
		// 2.1 reduce() - multiply using Method Reference
		int multiply2 = Arrays
				.stream(intArrayEmpty)
				.reduce(1, (m, n) -> m * n); // initial value 1
		// 2.2 print to console
		System.out.println("Stream.reduce() - Multiply on empty "
				+ "array with initial value 1 = " 
				+ multiply2);
		// 3. empty String array
		String[] strArrayEmpty = {};
		// 3.1 default/initial value
		String defaultValue = "Australia";
		// 3.2 Stream.reduce() - string concatenation using lambda
		String result3 = Arrays
				.stream(strArrayEmpty)
				.reduce(defaultValue, (str1, str2) -> str1 + " " + str2);
		// 3.3 print to console
		System.out.println("String concatenation with "
				+ "initial value 'Australia' = " 
				+ result3);
	}
}
Output:
Stream.reduce() - Sum on empty array with initial value 1 = 1
Stream.reduce() - Multiply on empty array with initial value 1 = 1
String concatenation with initial value 'Australia' = Australia
3.4 Map and Reduce 1 – to find average Age of a class
- A list contains 7 Student information with attributes like rollNo, name, and their age
 - Extract age from Student information using Stream’s map() method
 - And then we are going to reduce to get average age of class using Stream’s average() method which is one form of reduce() method
 
Student.java
package net.bench.resources.stream.reduce.example;
public class Student {
	// member variables
	private int rollNumber;
	private String name;
	private int age;
	// constructors
	// getters & setters
	// toString()
}
MapReduceOnStudentAverage.java
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
import java.util.List;
public class MapReduceOnStudentAverage {
	public static void main(String[] args) {
		// list of students
		List<Student> students = Arrays.asList(
				new Student(1, "Viraj", 17),
				new Student(2, "Krishnanand", 18),
				new Student(3, "Rishi", 19),
				new Student(4, "Suresh", 23),
				new Student(5, "Aditya", 22),
				new Student(6, "Rafat", 24),
				new Student(7, "Mandar", 21)
				);
		// print original Student list
		System.out.println("Original Student list :- \n");
		students.stream().forEach(System.out::println);
		// Stream.reduce() - to find Average age of class
		double averageAge = students
				.stream()
				.mapToInt(student -> student.getAge()) // map
				.average() // reduce
				.getAsDouble(); // return value as double
		System.out.format("\nThe average Age of class is = %.3f", averageAge);
	}
}
Output:
Original Student list :- 
Student [rollNumber=1, name=Viraj, age=17]
Student [rollNumber=2, name=Krishnanand, age=18]
Student [rollNumber=3, name=Rishi, age=19]
Student [rollNumber=4, name=Suresh, age=23]
Student [rollNumber=5, name=Aditya, age=22]
Student [rollNumber=6, name=Rafat, age=24]
Student [rollNumber=7, name=Mandar, age=21]
The average Age of class is = 20.571
3.5 Map and Reduce 2 – to find total amount of Grocery shopping
- A list contains Product information of 4 grocery items, each with attributes like id, name, quantity and price
 - We are going to calculate amount of each items purchased by multiplying quantity with its price using Stream’s map() method
 - Finally, we are finding total amount of all items purchased using Stream’s reduce() method (via Method reference Double::sum)
 
Product.java
package net.bench.resources.stream.reduce.example;
public class Product {
	// member variables
	private int productId;
	private String productName;
	private int quantity;
	private double price;
	// 4 arg parameterized constructor
	// getters & setters
	// toString()
}
MapReduceOnStudentAverage.java
package net.bench.resources.stream.reduce.example;
import java.util.ArrayList;
import java.util.List;
public class MapReduceOnGroceryTotalPrice {
	public static void main(String[] args) {
		// list of products
		List<Product> products = new ArrayList<>();
		// add products to cart
		products.add(new Product(101, "Lentil", 4, 16.52)); // 4 lentil packets
		products.add(new Product(102, "Wheat", 2, 33.31)); // 2 wheat packets
		products.add(new Product(103, "Rice", 2, 55.69)); // 2 rice packets
		products.add(new Product(104, "Oil", 3, 165.27)); // 3 oil packets
		// print Product list
		System.out.println("Grocery shopping list :- \n");
		products.stream().forEach(System.out::println);
		// Stream.reduce() - to find total amount of shopping
		double totalAmount = products
				.stream()
				.map(product -> product.getPrice() * product.getQuantity()) // map
				.reduce(Double.MIN_VALUE, Double::sum); // reduce
		// total amount of shopping
		System.out.format("\nThe total shopping amount is = %.2f", 
				totalAmount);
	}
}
Output:
Grocery shopping list :- 
Product [productId=101, productName=Lentil, quantity=4, price=16.52]
Product [productId=102, productName=Wheat, quantity=2, price=33.31]
Product [productId=103, productName=Rice, quantity=2, price=55.69]
Product [productId=104, productName=Oil, quantity=3, price=165.27]
The total shopping amount is = 739.89
4. Stream reduce() method with Identity, accumulator and combiner :
- This is the third variant of the reduce() method which takes three arguments 
1. Identity as initial/default value
2. BiFunction as accumulartor
3. BinaryOperator as combiner - This is mainly used in Parallel Stream
 - This is very much similar to 2nd variant except a combiner to combine the result when working in Parallel Stream
 - In Parallel Stream, streams are sub-divided into sub-streams and further computation/aggregate function are executed to achieve the target, so to combine the result we need combiner
 - Identity value will be used as a starting value for combiner
 - Method signature :- <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
 
4.1 Math operation in Parallel Stream
- First, we are going to calculate sum of all Stream elements with starting value of 0
 - Second, we are going to multiply all Stream elements with starting value of 1
 - We can use either Lambda Expression or Method Reference for these Math operations
 
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
import java.util.List;
public class ReduceWithParallelStream {
	public static void main(String[] args) {
		// List of Integers
		List<Integer> numbers = Arrays.asList( 
				10, 20, 30, 40, 50
				);
		// 1.1 reduce() - sum using combiner
		int sum = numbers
				.parallelStream() // parallel stream
				.reduce(0,  // initial value 0
						(x, y) -> x + y, // accumulator
						Integer::sum // combiner
						); 
		System.out.println("Parallel Stream reduce() - "
				+ "Sum with initial value 0 = " 
				+ sum);
		// 1.3 reduce() - multiply using combiner
		int multiply = numbers
				.parallelStream() // parallel stream
				.reduce(1, // initial value 1
						(m, n) -> m * n, // accumulator
						(A, B) -> A * B // combiner
						); 
		System.out.println("Parallel Stream reduce() - "
				+ "Multiply with initial value 1 = " 
				+ multiply);
	}
}
Output:
Parallel Stream reduce() - Sum with initial value 0 = 150
Parallel Stream reduce() - Multiply with initial value 1 = 12000000
5. Filter, Map and Reduce :
- In this example, we are going to filter based on some criteria and then extract some value with map and finally reducing to get final desired value
 
5.1 Get total/average salary and count of employees working in IT department
- A list of employees is defined with attributes like id, name, department and their salary
 - Filter – we are filtering to get employees of IT department
 - Map – extracting salary information from employee list after filtering
 - Reduce 1 – finding total salary of all IT department employees
 - Reduce 2 – finding count of IT department employees
 - Reduce 3 – finding average salary of IT department employees and returning double value to get accurate result with fractional portion
 
Employee.java
package net.bench.resources.stream.reduce.example;
public class Employee {
	// member variables
	private int id;
	private String name;
	private String department;
	private long salary;
	// 4 arg parameterized constructor
	// getters & setters
	// toString()
}
FilterMapReduceOnEmployeeSalaryAverage.java
package net.bench.resources.stream.reduce.example;
import java.util.Arrays;
import java.util.List;
public class FilterMapReduceOnEmployeeSalaryAverage {
	public static void main(String[] args) {
		// 1. list of employees
		List<Employee> employees = Arrays.asList(
				new Employee(101, "Krish", "IT", 87000),
				new Employee(102, "Viraj", "Admin", 62000),
				new Employee(103, "Suresh", "IT", 76000),
				new Employee(104, "Aditya", "IT", 81000),
				new Employee(105, "Rishi", "HR", 68000)
				);
		// 1.2 print original Student list
		System.out.println("Employees list :- \n");
		employees.stream().forEach(System.out::println);
		// 2. get TOTAL salary of IT employees
		long totalSalaryOfItEmp = employees
				.stream()
				.filter(employee -> employee.getDepartment()
						.equalsIgnoreCase("IT")) // filter
				.mapToLong(employee -> employee.getSalary()) // map
				.sum(); // reduce
		// 2.1 print to console
		System.out.println("\nThe Total salary of employees"
				+ " working in IT department is = " 
				+ totalSalaryOfItEmp);
		// 3. get COUNT of IT employees
		long countOfItEmp = employees
				.stream()
				.filter(employee -> employee.getDepartment()
						.equalsIgnoreCase("IT")) // filter
				.count(); // reduce
		// 3.1 print to console
		System.out.println("\nCount of employees"
				+ " working in IT department is = " 
				+ countOfItEmp);
		// 4. get AVERAGE salary of IT employees
		double average = employees
				.stream()
				.filter(employee -> employee.getDepartment()
						.equalsIgnoreCase("IT")) // filter
				.mapToLong(employee -> employee.getSalary()) // map
				.average() // reduce
				.getAsDouble(); // return double
		// 4.1 print to console
		System.out.println("\nThe average salary of employees"
				+ " working in IT department is = " 
				+ average);
	}
}
Output:
Employees list :- 
Employee [id=101, name=Krish, department=IT, salary=87000]
Employee [id=102, name=Viraj, department=Admin, salary=62000]
Employee [id=103, name=Suresh, department=IT, salary=76000]
Employee [id=104, name=Aditya, department=IT, salary=81000]
Employee [id=105, name=Rishi, department=HR, salary=68000]
The Total salary of employees working in IT department is = 244000
Count of employees working in IT department is = 3
The average salary of employees working in IT department is = 81333.33333333333
References:
- https://docs.oracle.com/javase/tutorial/collections/streams/reduction.html
 - https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
 - https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html
 - https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html
 
Happy Coding !!
Happy Learning !!