Before we look into Java Stream API Examples, let’s see why it was required. Suppose we want to iterate over a list of integers and find out sum of all the integers greater than 10. All the Java Stream API interfaces and classes are in the java.util.stream package.
Prior to Java 8, the approach for getting 1 to 10 sum, it would be:
int addNumbersNojdk8(List< Integer > list) {
int sum = 0;
for (int number : list) {
if (number > 10) {
sum = sum + number;
}
}
return sum;
}
After java 8 it would be:
int addNumbersWithjdk8(List< Integer > list) {
return list.stream().filter(p->p>10).mapToInt(p -> p).sum();
}
One more example where we will see use of IntStream. Let’s say we want to find a valid string having only alphabets.
package com.javabykiran.jdk8.ex;
public class testStringWithAlphabets {
public static boolean isStringOnlyAlphabet(String str){
return ((str != null) && (!str.equals("")) && (str.chars().allMatch(Character::isLetter)));
}
public static void main(String[] args) {
System.out.println(isStringOnlyAlphabet("kiran"));
}
}
In above program highlighted allMatch is from Stream package and chars method is added in jdk1.8.
Now we need to understand about functional programing. Java 8 supports functional programing where Stream API’s are majorly used.
Stream explanation will be discussed in later part of this chapter.
Program with Functional Programming
public double CalWithFunctionalPrograming(double x,double y){
return x * x + y * y;
}
Program without Functional Programming
public double CalWithOutFunctionalPrograming(double x,double y) {
double z=x+x;
double z1=y+y;
double z2=z+z1;
return z2;
}
Remember for functional programming:
F(x)=y or g(f(y(x))) = z
Before proceeding, one more concept we need to understand is functional interface. An interface with exactly one abstract method is called Functional Interface.
@FunctionalInterface annotation is added so that we can mark an interface as functional interface.
It is not mandatory to use it, but it’s recommended to avoid addition of extra methods accidentally. If the interface is annotated with @FunctionalInterface annotation and we try to have more than one abstract method, it throws compiler error.
Java 8 Collections API has been rewritten and new Stream API is introduced that uses a lot of functional interfaces. Java 8 has defined a lot of functional interfaces in java.util.function Package. Some of the useful java 8 functional interfaces are Consumer, Supplier, Function and Predicate.
java.lang.Runnable is a great example of functional interface with single abstract method run().
@FunctionalInterface
public interface MyFirstFunctionalInterface {
public void firstWork();
}
Also, since default methods are not abstract you’re free to add default methods to your functional interface as many as you like.
Below program will have compile time errors.
package com.javabykiran.FunctionInterfaceEx;
@FunctionalInterface
public interface TestI {
int add(int a, int b);
int sub(int a, int b); // Not allowed
}
Try to understand below example :
package com.javabykiran.FunctionInterfaceEx;
@FunctionalInterface
public interface TestI {
int doOperation(int a, int b); //only one allowed
default String multiply() { // have a body
return null;
}
default String divide() { // have a body
return null;
}
@Override
public String toString();
// toString can’t have body as it is from object class
}
Summarizing below points about functional interface.
Functional interface have only one abstract method, No need to count object class methods here [toString method in above example]
Can have any no of default methods [default methods with body]
Cannot have same name for default methods for those methods which are in object class [in above example toString method we just write without body]
@FunctionalInterface annotation is added so that we can mark an interface as functional interface which is not mandatory.
After functional interface understanding we will try to understand Lambda expressions. Lambda expression facilitates functional programming, and simplifies the development a lot.
Syntax:
Lambda expressions are used primarily to define inline implementation of a functional interface, i.e., an interface with a single abstract method only.
How to use functional interface with lambda expressions
package com.javabykiran.FunctionInterfaceEx;
@FunctionalInterface
public interface TestI {
int doOperation(int a, int b);
}
package com.javabykiran.FunctionInterfaceEx;
public class TestImpl {
public static void main(String[] args) {
TestI testI = (a, b) -> a + b;
System.out.println(testI.doOperation(334, 556));
TestI testISub = (a, b) -> a - b;
System.out.println(testISub.doOperation(334, 556));
}
}
Question: What advantage you think we get from functional interface and lambda expression here?
Answer: We can have different implementation of doOperation method inline here. Anonymous inner class can also do same but lot more code we needed to write here.
We will see different flavors to call this operation.
public class TestImpl {
public static void main(String[] args) {
// with type declaration
TestI add = (int a, int b) -> a + b;
// without type declaration
TestI sub = (a, b) -> a - b;
// with return statement along with curly braces
TestI mul = (a, b) -> {return a + b;};
//without return statement and without curly braces
TestI divide= (a, b) -> a / b;
}
}
Example to use default method
public interface TestI {
default String multiply() {
return “I am in TestI mulitply”;
}
}
public class TestImplTest implements TestI {
public static void main(String[] args) {
TestImplTest tit = new TestImplTest();
tit.multiply();
}
}
Output:
I am in TestI mulitply
// If TestImplTest wants to implements its own behavior then
// we can implement it as below
public class TestImplTest implements TestI {
String multiply() {
return “I am in TestImplTest mulitply”;
}
public static void main(String[] args) {
TestImplTest tit = new TestImplTest();
tit.multiply(); // calling default method
}
}
Output:
I am in TestImplTest multiply
We can have only static or default or abstract method in interfaces in jdk 8.
Static or default method will have a body
Only one keyword can be used at a time. Static or default
You can define static default methods in interface which will be available to all instances of class which implement this interface.
This makes it easier for you to organize helper methods in your libraries; you can keep static methods specific to an interface in the same interface rather than in a separate class.
This enables you to define methods out of your class and yet share with all child classes.
They provide you a highly desired capability of adding a capability to number of classes without even touching their code. Simply add a default method in interface which they all implement.
Example to use static default method.
package com.javabykiran.FunctionInterfaceEx;
public interface TestI {
static String multiply() {
return null;
}
default String divide() {
return null;
}
@Override
public String toString();
}
public class TestImplTest {
public static void main(String[] args) {
TestI.multiply();// calling static method
}
}
Majorly we use three methods in this- map, filter and collect method
See below example :
ArrayList< Employee > alEmpList = new ArrayList<> ();
ArrayList < Integer > alEmp = (ArrayList< Integer >)
alEmpList.stream().map(age -> age.getAge()).filter(age -> age > 30).collect(Collectors.toList());
Explanation is ,
In this example we want to filter all age greater than 30.Expectation is output should be all numbers greater than 30.
Stream Method : It is converting array list of Employees into small chunks and making it ready for next processing.
Map method : It is used to take out some elements from stream of collection. With example we will try to understand.
ArrayList contain Employee and Employee contains age if we use map on Stream of ArrayList then we get collection of age.
ArrayList contain Employee and Employee contains Phone and Phone contain mobile no as integer if we use map on Stream of ArrayList then we get collection of Phone then again use Map on Phone to get collection of mobile nos.
alEmpList.stream().map(phone->phone.getPhone())
.map(mobileNo->mobileNo.getMobileNo())
.filter(mobileNo -> mobileNo > 30)
.collect(Collectors.toList());
Filter Method : It is used to filters a data from map here we can use lambda expressions to make it short. We can pass predicates as well here see next section.
Collect Method : It is used to collection all elements in a list.
Below is complete example for all above methods :
package com.javabykiran;
public class Employee {
int age;
String loc;
String name;
Phone phone;
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
public Employee() {
super();
// TODO Auto-generated constructor stub
}
public Employee(int age, String loc, String name) {
super();
this.age = age;
this.loc = loc;
this.name = name;
}
public Employee(int age, String loc, String name, Phone phone) {
super();
this.age = age;
this.loc = loc;
this.name = name;
this.phone = phone;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Employee [age=" + age + ", loc=" + loc + ", name=" + name + ", phone=" + phone + "]";
}
}
package com.javabykiran;
public class Phone {
int mobileNo;
public int getMobileNo() {
return mobileNo;
}
public void setMobileNo(int mobileNo) {
this.mobileNo = mobileNo;
}
@Override
public String toString() {
return "Phone [mobileNo=" + mobileNo + "]";
}
}
Above classes were just POJO now we will see how we can do different operations with collections
package com.javabykiran;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class MapFilterCollectEx {
public static ArrayList< Employee > createEmployees(){
ArrayList< Employee > alEmp = new ArrayList<>();
Phone ph=new Phone();
ph.mobileNo=963720;
alEmp.add(new Employee(20, "Pune", "Kiran",ph));
Phone ph1=new Phone();
ph1.mobileNo=889574;
alEmp.add(new Employee(46, "Nagpur", "Kiran1",ph1));
Phone ph2=new Phone();
ph2.mobileNo=986895;
alEmp.add(new Employee(34, "Mumbai", "Kiran2",ph2));
return alEmp;
}
// return all emp of more age than 30
public static ArrayList< Employee > allEmpAge() {
ArrayList< Employee > alEmpList = createEmployees();
alEmpList = (ArrayList< Employee >)
alEmpList.stream().filter(finalAge -> finalAge.getAge()>30)
.collect(Collectors.toList());
return alEmpList;
}
// return all elements
public static List< Integer > allEmpMobileNo() {
ArrayList< Employee > alEmpList = createEmployees();
List< Integer > alEmp = alEmpList.stream().map
(phone ->phone.getPhone()).map(mobileNo -> mobileNo.getMobileNo()).filter(mobileNo ->
mobileNo > 30).collect(Collectors.toList());
return alEmp;
}
// return all emp of more age than 30
public static ArrayList< Integer > allEmpAgeGreaterThan30() {
ArrayList< Employee > alEmpList = createEmployees();
ArrayList< Integer > alEmp = (ArrayList< Integer >) alEmpList.stream().map(age -> age.getAge())
.filter(age -> age > 30).collect(Collectors.toList());
return alEmp;
}
public static void main(String[] args) {
for (int age : allEmpAgeGreaterThan30()) {
System.out.println(age);
}
for (Employee empAge : allEmpAge()) {
System.out.println(empAge.getAge());
}
for (int empMobNo : allEmpMobileNo()) {
System.out.println(empMobNo);
}
}
}
Above classes were just POJO now we will see how we can do different operations with collections
int addNumbersWithjdk8(List< Integer > list) {
return list.stream()
.filter(p->p>10)
.mapToInt(p -> p).sum();
}
List< Integer > getListGreaterthan10(List< Integer > list){
return list.stream()
.filter(p->p>10)
.collect(Collectors.toList());
}
How to design predicate :
public static Predicate< Employee > getAllOldEmp() {
return p -> p.getAge() > 30;
}
public static Predicate< Employee > getAllOldEmp1() {
return p -> p.getAge() > 45;
}
In this case P can be anything may be x or y or z.
P indicates Employee in this case. Automatically it behaves like employee object.
Now to filter employees how I can use above predicate?
public static List< Employee > filterEmployees(List< Employee > employees, Predicate< Employee > predicate) {
return employees.stream().filter (getAllOldEmp())
.collect (Collectors.toList ());
}
Above method filters employee as per predicates provided.
Some more simple examples of predicate
public class PredicateExample1 {
public static void main(String[] args) {
//Predicate String
Predicate< String > predicateString =
s -> { return s.equals("Hello"); };
System.out.println(predicateString.test("Hello"));
System.out.println(predicateString.test("Hello World"));
//Predicate integer
Predicate< Integer > predicateInt =i -> { return i > 0; };
System.out.println(predicateInt.test(5));
System.out.println(predicateInt.test(-5));
}
}
Output:
true
false
true
false