You are on page 1of 34

Advanced Java Programming

Topic: Lambda Operations

By
Shruti
Asst. Professor, LPU
Contents
 Extracting data from an object using map
 Describing the types of stream operations
 Describing the Optional class
 Describing lazy processing
 Sorting a stream
 Saving results to a collection using the collect method
 Grouping and partition data using the Collectors class
Reduction Operations
 min(), max() and count() represents the reduction operation in
Stream.

 Reduction operation reduces the stream to a single value in


case of min, max and count.

 The stream API generalizes this concept by providing the


reduce( ) method.

 By using reduce( ), we can return a value from a stream based


on any arbitrary criteria.
Using reduce method
 Optional<T> reduce(BinaryOperator<T> accumulator)
returns an object of type Optional, which contains the result.

 T reduce(T identityVal, BinaryOperator<T> accumulator)


returns an object of type T (the element type of the stream).

NOTE:
BinaryOperator is a functional interface that extends the
BiFunction functional interface. It defines the apply method.
T apply(T val, T val2)

 Here, val stores the current result and val2 contains the next element.
import java.util.*; import java.util.stream.*;
class StreamDemo {
public static void main(String[] ar) {
ArrayList<Integer> myList = new ArrayList<>( );
myList.add(7); myList.add(18); myList.add(10);
myList.add(24); myList.add(17); myList.add(5);

Optional<Integer> productObj = myList.stream().reduce((a,b) -> a*b);


if(productObj.isPresent())
System.out.println("Product as Optional: " + productObj.get());

int product = myList.stream().reduce(1, (a,b) -> a*b);


System.out.println("Product as int: " + product);
} }
 identityVal is a value such that an accumulator operation
involving identityVal and any element of the stream yields that
element, unchanged.

 For example, if the operation is addition, then the identity


value will be 0 because 0 + x is x.

 For multiplication, the value will be 1, because 1 * x is x.


Let’s Do Something…
 Write code snippet to obtain the product of all the even
numbers stored in an arraylist using stream operations and
Lambda expression.

int evenProduct = myList.stream().reduce(1, (a,b) -> {


if(b%2 == 0) return a*b;
else return a;
});
Parallel Streams

 The parallel execution of code via multi-core processors can


result in a substantial increase in performance.

 One of the benefits that the stream library offers is the ability
to easily and reliably parallel process certain operations.

 Parallel processing of a stream is quite simple to request: just


use a parallel stream.
Creating Parallel Streams

1. Using parallelStream() method


defined by Collection.

2. Using parallel() method


defined by BaseStream.

NOTE:
 It returns a parallel stream based on the sequential stream that
invokes it.
 If it is called on a stream that is already parallel, then the invoking
stream is returned.
Important

 Any operation applied to a parallel stream must be stateless.

 It should also be non-interfering and associative.

 Stateless means that the operation does not rely on any state
information. Thus, each element is processed independently.

 Non-interfering means that the data source is not modified by


the operation.
Using Parallel Streams

 When using parallel streams, the following version of reduce( )


is helpful. It gives you a way to specify how partial results are
combined:

<U> U reduce(U identityVal, BiFunction<U, ? super T, U>


accumulator, BinaryOperator<U> combiner)

 In this version, combiner defines the function that combines two


values that have been produced by the accumulator function.
Let’s Do Something…
Write code snippet to find out the product square-roots of all the
elements of an arraylist containing integers.

double productOfSqrRoots2 = myList.stream().reduce(


1.0,
(a,b) -> a * Math.sqrt(b));

Now use parallel streams for the same task…


Example

double result =
myList.parallelStream().reduce(1.0,
(a,b) -> a * Math.sqrt(b),
(a,b) -> a * b
);

NOTE:
We can switch a parallel stream to sequential by calling the
sequential( ) method, which is specified by BaseStream.
S sequential( )
Important

double productOfSqrRoots2 =
myList.parallelStream().reduce( 1.0,
(a,b) -> a * Math.sqrt(b));

//This won’t work…

 In this version of reduce( ), the accumulator and the combiner function


are one and the same.
 This results in an error because when two partial results are combined,
their square roots are multiplied together rather than the partial results,
Mapping
Mapping
 It is useful to map the elements of one stream to another.

<R> Stream<R> map(Function<? super T, ? extends R> mapFunc)

 Here, R specifies the type of elements of the new stream


 T is the type of elements of the invoking stream
 mapFunc is an instance of Function, which does the mapping.

 The map function must be stateless and non-interfering.


Example

// Map the square root of the elements in myList to a new stream.


Stream<Double> sqrtRootStrm = myList.stream().map(
(a) -> Math.sqrt(a));

// Find the product of the square roots.


double productOfSqrRoots =
sqrtRootStrm.reduce(1.0, (a,b) -> a*b);
Note
 The difference between this version and the previous is simply
that the transformation (i.e., the computation of the square roots)
occurs during mapping, rather than during the reduction.

 Because of this, it is possible to use the two-parameter form of


reduce( ) to compute the product because it is no longer
necessary to provide a separate combiner function.
Example
import java.util.*;
import java.util.stream.*;
class NamePhoneEmail
{
String name, phonenum, email;
NamePhoneEmail(String n, String p, String e)
{
name = n;
phonenum = p;
email = e;
}
}
class NamePhone
{
String name;
String phonenum;
NamePhone(String n, String p)
{
name = n;
phonenum = p;
}
}
class MappingDemo
{
public static void main(String[] args)
{
ArrayList<NamePhoneEmail> myList = new ArrayList<>( );
myList.add(new NamePhoneEmail(“Ravi", “11”, “r@x.com"));
myList.add(new NamePhoneEmail("Jay", "54“, “J@dt.com"));
myList.add(new NamePhoneEmail("Mary", “533“, “M@yahoo.com"));

Stream<NamePhone> nameAndPhone = myList.stream().map(


(a) -> new NamePhone(a.name,a.phonenum) );
}
}
Collecting
Using Collect() method

collect(Supplier<R> supplier, BiConsumer<R,? super T>


accumulator, BiConsumer<R,R> combiner)

collect(Collector<? super T, A, R> collector)

 This method performs a mutable reduction operation


(accumulates (or collects) input elements into a mutable result
container)
on the elements of this stream using a Collector.
Using Collect() method

List<String> letters = new ArrayList<>();


letters.add("H"); letters.add("e");
letters.add("l"); letters.add("l");
letters.add("o");

List<String> word = letters.stream().map(s ->


s.toUpperCase()).collect(Collectors.toList());
Collectors Class

 Collectors implements many useful reduction operations.

//Collecting in a List
List<String> word = letters.stream().map(s ->
s.toUpperCase()).collect(Collectors.toList());

//Collecting in a Set
Set <String> word= letters.stream().map(s ->
s.toUpperCase()).collect(Collectors.toSet());
Grouping and Partitioning
Using Collectors

// Group by starting letter

Map<String, List<String>> grouped =


letters.stream() .collect(Collectors.groupingBy(s-
>s.substring(0,1)));

// Partition letters into uppercase and lowercase

Map<Boolean, List<String>> upperLower =


letters.stream().collect(Collectors.partitioningBy(s ->
Character.isUpperCase(s.codePointAt(0))));
Let’s Do Something…

WAP to create a class Employee having attributes name, gender,


id and salary. Store the 5 Employee objects in an ArrayList.
Use Streams to -

1. Display the names of all the Male employees having salary less
than 25000 in descending order of their id.

2. Group the Employees on the basis of salary with range 10-20 K


20-30K and 30-40K.
SPLITERATORS

 Spliterator offers an alternative to Iterator, especially when


parallel processing is involved.

 In general, Spliterator is more sophisticated than Iterator.


Using Spliterators

 Spliterator defines mainly three methods:

1. boolean tryAdvance(Consumer<? super T> action)

2. default void forEachRemaining(Consumer<? super T> action)

3. Spliterator<T> trySplit( )
tryAdvance()
 It performs an action on the next element and then advances the
iterator.

 tryAdvance( ) returns true if there is a next element. It returns


false if no elements remain.

Example:
Stream<String> myStream = myList.stream();
Spliterator<String> splitItr = myStream.spliterator();
while(splitItr.tryAdvance( s -> System.out.println(s) );
forEachRemaining()
 Used to perform some action on each element collectively,
rather than one at a time.

 This method applies action to each unprocessed element and then


returns.

splitItr.forEachRemaining((n) -> System.out.println(n));

 This method eliminates the need to provide a loop to cycle


through the elements one at a time. This is the advantage of
Spliterator.
trySplit ()
 It splits the elements being iterated in two, returning a new
Spliterator to one of the partitions.

 The other partition remains accessible by the original Spliterator.

Spliterator<T> trySplit( )

 If it is not possible to split the invoking Spliterator, null is


returned.
 Otherwise, a reference to the partition is returned.
Let’s Do Something…

 WAP in which display the salary of all the employees in sorted


order of their ID.

 Given that each Employee Object contains the Name, Id and


Salary attribute. And an arraylist contains the N employee
objects.

You might also like