Skip to content

Java Learning Generics Streams

Vidyasekaran Duvarakan edited this page Jan 12, 2021 · 10 revisions

Generics

https://www.youtube.com/watch?v=XMvznsY02Mk

and

https://www.youtube.com/watch?v=h7piyWnQbZA

Used to achieve type safety, Code reusablity

Below code is not type safe - Below Code will get Runtime error

List val = new ArrayList(); val.add(5); val.add("Name");

// You will get run time error while converting string to integer but at compile time no error

int i = Integer.parseInt(val.get(1).toString());

How you achieve Type Safety

Mention Generics here so you will get compile type error while adding different object type

List val = new ArrayList();

val.add(5);

val.add("Name"); // You will get compile type error

Write a class using Generics which accepts any type of object. If not you will have to write copy of this same class for every type

example Integer, Double, Float, String etc. The same concept applies to Class, Methods etc..

class Container { T value;

Container(T value) { this.value = value; }

public void show() { System.out.println(value.getClass().getName());

} }

public class GenericsDemo { public static void main(String[] args) { Container obj = new Container<>(); obj.value=9; obj.show(); } }

Write a class using Generics which accepts any type of object.

Restricting a class to only accept Specific Type - Now below class can only accept Number object type or its childrens.

class Container { T value;

public void show() { System.out.println(value.getClass().getName());

}

public void demo(ArrayList obj) {

}

public void demo1(ArrayList<? extends Number> obj) {

} }

public class GenericsDemo { public static void main(String[] args) {

     Container<Integer> obj = new Container<>();
     obj.value=9;
     obj.show();
     
     obj.demo(new ArrayList<Integer>());  // Works fine
     obj.demo(new ArrayList<Double>());   // Throws Error
     obj.demo1(new ArrayList<Integer>());   // Fine
     obj.demo1(new ArrayList<Double>());   // Fine
 obj.demo1(new ArrayList<String>());   // Compile time error - only Number and subclass accepted

     Container<Student> obj = new Container<>();   //Compile Time ERROR
}

}

You can pass any number of arguments such as T, S to the class like below .

class Container<T extends Number, S extends String> { T value; S value1;

HashMap<T,S> map = new HashMap<>();

public void show() { System.out.println(value.getClass().getName());

}

Container obj = new Container<>(10,"Hello");


Java Streams

map() - covert one type to another

Get a Student type as input and transform it to string

List studentNames = StudentDataBase.getAllStudents().stream().map(Student::getName).collect(toList());

Perform uppercase operation on each name of the student

List studentNames = StudentDataBase.getAllStudents().stream().map(Student::getName). .map(String::toUpperCase) collect(toList());

If i want a Set instead of List

Set studentNames = StudentDataBase.getAllStudents().stream().map(Student::getName). .map(String::toUpperCase) collect(toSet());

Stream API : flatMap()

flatMap : Converts (Transforms) one type to another as like map() method

Used in the context of stream where each element in the stream represents multiple elements.

Example:

Each Stream element represents multiple elements. Flat map can used to flatten list of list to single list or set etc

. Stream . Stream

class StudentDataBase {

public static List getAllStudents(){

Student student1 = new Student("adam",Arrays.asList("swimming","basketball","volleyball")); Student student2 = new Student("adam",Arrays.asList("cricket","beach volleyball","swimming"));

List students = Arrays.asList(student1,student2); return students; }

public static List printStudentActivities(){

   List<String> studentActivities = StudentDataBase.getAllStudents().stream()
.map(Student::getActivities)
.flatMap(List::stream)
.collect(toList());

return studentActivities;

Syout("Flat map"+printStudentActivities());

If you want to find number of elements in a stream

long size = StudentDataBase.getAllStudents().stream() .map(Student::getActivities) .flatMap(List::stream) .distinct() .count();

If you want to sort all student activities in a Natural sorted order

   List<String> studentActivities = StudentDataBase.getAllStudents().stream()
.map(Student::getActivities)
.flatMap(List::stream)
.sorted()
.collect(toList());

Customized Sort where we sort students by name Ascending

   List<String> studentActivities = StudentDataBase.getAllStudents().stream()
.map(Student::getActivities)
.flatMap(List::stream)
.sorted(Comparator.comparing(Student::getName))
.collect(toList());

Customized Sort where we sort students by gpa ascending

   List<String> studentActivities = StudentDataBase.getAllStudents().stream()
.map(Student::getActivities)
.flatMap(List::stream)
.sorted(Comparator.comparing(Student::getGpa))
.collect(toList());

Customized Sort where we sort students by gpa descending

   List<String> studentActivities = StudentDataBase.getAllStudents().stream()
.map(Student::getActivities)
.flatMap(List::stream)
.sorted(Comparator.comparing(Student::getGpa).reversed())
.collect(toList());

Stream API - filter()

. filter - filters the elements in the stream.

Input to the filter is a Predicate functional interface

Predicate Example

https://www.geeksforgeeks.org/java-8-predicate-with-examples/

  • Creating predicate Predicate lesserthan = i -> (i < 18);

    // Calling Predicate method System.out.println(lesserthan.test(10));

Predicate femaleStudent = student::getGender().equals("female"))

List studentActivities = StudentDataBase.getAllStudents().stream() .filter((student -> student.getGender().equals("female"))) (or) .filter(femaleStudent) //filters and sends only the students whose gender is female .collect(toList());

U can also chain filter i.e from Female students if you want those who got gpa > 3

List studentActivities = StudentDataBase.getAllStudents().stream() .filter((student -> student.getGender().equals("female"))) //filters and sends only the students whose gender is female .filter(student -> student.getGpa()>=3.9) .collect(toList());

Find Student having highest GPA - Note we don't have initial value in reduce so we need to return Optional

public static Optional getHighestGPAStudent(){

// reduce gets students gpa one by one and compares by keeping highest gpa and compares it with rest of the stream

return StudentDataBase.getAllStudents().stream().reduce((s1,s2) ->{ if(s1.getGpa() > s2.getGpa()){ return s1; }else{ return s2; } }); Optional student = getHighestGPAStudent();

More Readable of above to find student with highest gpa

public static Optional getHighestGPAStudent(){ return StudentDataBase.getAllStudents().stream().reduce((s1,s2) ->(s1.getGpa() > s2.getGpa()) ? s1 : s2);

Optional student = getHighestGPAStudent(); }

Performing map, filter and reduce operation

int numberOfNoteBooks = StudentDataBase.getAllStudents().stream().map(Student::getNoteBooks) //Stream .reduce(0,(a,b) -> a+b);

or use like below with Integer::sum

int numberOfNoteBooks = StudentDataBase.getAllStudents().stream().map(Student::getNoteBooks) //Stream .reduce(0,Integer::sum);

Apply filter to above to figure out sum of notebooks received by students having grade level > 3

int numberOfNoteBooks = StudentDataBase.getAllStudents().stream() .filter((student -> student.getGradeLevel()>=3)) .filter((student -> student.getGender().equals("female")) .map(Student::getNoteBooks) //Stream .reduce(0,Integer::sum);

limit() and skip()

These 2 functions helps to create a sub stream.

limit(n) - limits the "n" numbers of elements to be processed in the stream. Example: If you have 1000 elements and want to process only first 100

skip(n) - skips the "n" number of elements from the stream.

List integers = Arrays.asList(6,7,8,9,10);

//Limits streams to just 2 elements i.e 6, 7 Optional limitResult = integers.stream().limit(2).reduce((x,y) -> x+y);

if(limitResult.isPresent()){ S.o.p(""+limitResult.get()); } else{ S.o.p("no element passed"); }

Skip - if you want to skip first 5 elements and process rest

List integers = Arrays.asList(6,7,8,9,10);

//Limits streams to just 2 elements i.e 6, 7 Optional limitResult = integers.stream().skip(3).reduce((x,y) -> x+y);

if(limitResult.isPresent()){ S.o.p(""+limitResult.get()); } else{ S.o.p("no element passed"); }

Stream API

  • All these functions takes in a predicate as an input and returns a Boolean as an output.

. anyMatch() - Returns true if any one of the element matches the Predicate, otherwise false.

. allMatch() - Returns true if all the elements in the stream matches the predicate, otherwise false.

. noneMatch() - Just opposite to allMatch(). Returns true if none of the element in the stream matches the predicte, otherwise false.

Example:

StudentDataBase.getAllStudents().stream().allMatch(student -> student.getGpa() >= 3.9);

Stream API

  • findFirst() and findAny()

Used to find an element in the stream.

Both the functions return the result of type Optional.

  • findFirst() - returns first element in the stream.

  • findAny() - returns the first encountered element in the stream.

It differs in the parallel stream only.

Example:

StudentDataBase.getAllStudents().stream().filter(student -> student.getGpa()>=3.9)

.findAny();

Optional studentOptionalfind = findAnyStudent();

if(studentOptionalfind.isPresent()){

S.o.p("Found the student"+studentOptionalfind.get()); }

Java 8 | Consumer Interface in Java with Examples

https://www.geeksforgeeks.org/java-8-consumer-interface-in-java-with-examples/

Consumer - The Consumer Interface is a part of the java.util.function package which has been introduced since Java 8, to implement functional programming in Java. It represents a function which takes in one argument and produces a result. However these kind of functions don’t return any value.

Consumer display = a -> System.out.println(a); display.accept(10); // Implement display using accept()

Consumer Example :

https://github.com/dilipsundarraj1/java-8/blob/master/java-8/src/com/learnJava/functionalInterfaces/ConsumerExample.java

Biconsumer - It represents a function which takes in two arguments and produces a result. However these kind of functions don’t return any value.

https://github.com/dilipsundarraj1/java-8/blob/master/java-8/src/com/learnJava/functionalInterfaces/BiConsumerExample.java

Predicate - The Functional Interface PREDICATE is defined in the java.util.Function package. It improves reusablity, manageability of code, helps in unit-testing them separately, and contain some methods like:

static Predicate isEqual(Object targetRef)

default Predicate and(Predicate other)

default Predicate negate()

default Predicate or(Predicate other)

test(T t)

// Creating predicate Predicate lesserthan = i -> (i < 18);

// Calling Predicate method System.out.println(lesserthan.test(10));

NOTE: U can create multiple Predicates and Chain it so we get SQL and kinda functionality

https://github.com/dilipsundarraj1/java-8/blob/master/java-8/src/com/learnJava/functionalInterfaces/PredicateExample.java

Combining Predicate and Consumer https://github.com/dilipsundarraj1/java-8/blob/master/java-8/src/com/learnJava/functionalInterfaces/PredicateAndConsumerExample.java

Clone this wiki locally