Sunday, 14 June 2020

Stream - filter(), map(), collect(), min(), max()


1) Filter:

filter() is used to filter data in stream. This method takes a Predicate as parameter. The Predicate interface contains a function called test() which the lambda expression passed as parameter is matched against. In other words, the lambda expression implements the Predicate.test() method.

stream.filter( item -> item.startsWith("o") ); 

It takes a single parameter and returns a boolean. If you look at the lambda expression above, you can see that it takes a single parameter item and returns a boolean - the result of the item.startsWith("o") method call.


2) Map:
map() is used to perform transformation for the items in a collection to other objects.

items.stream().map( item -> item.toUpperCase() )

In above stream items in stream is transformed to uppercase.

3) Collect:
when collect() method is invoked, the filtering and mapping will take place and the object resulting from those actions will be collected.

List<String> filtered = items.stream().filter( item -> item.startsWith("o") )
                                    .collect(Collectors.toList());

4) Min and Max:
The min() and max() methods take a Comparator as parameter. The Comparator.comparing() method creates a Comparator based on the lambda expression passed to it. In fact, the comparing() method takes a Function which is a functional interface suited for lambda expressions. It takes one parameter and returns a value.

The min() and max() methods return an Optional instance which has a get() method on, which you use to obtain the value. In case the stream has no elements the get() method will return null.

String shortest = items.stream()
        .min(Comparator.comparing(item -> item.length()))
        .get();

Inbuilt method:
List<Integer> list = Arrays.asList(-9, -18, 0, 25, 4); 
  
/* Using stream.min() to get minimum element according to provided Integer Comparator */
Integer var = list.stream().min(Integer::compare).get(); 
System.out.print(var); 

Wednesday, 10 June 2020

Stream Operations

When ever we operate on data we will have basic CURD operation. Let's see how we are doing CURD in streams:

1) Create a stream:

List<Integer> lstInt = Arrays.asList(1,2,3,4,5,6,7,8);

lstInt.stream().forEach(System.out::println);

Stream has 2 steps - Configuring and Processing

In configuring phase, we would apply non terminal operations - filters and mappings

In processing phase, operation applied and result is produced

List<Integer> lstInt = Arrays.asList(1,2,3,4,5,6,7,8);

lstInt.stream().filter( item -> item > 5 ).forEach(System.out::println);

filter() - non terminal operations
forEach() - terminal operations

2) Update a stream: Updation is not removing actual elements from stream but filtered and givin what is expected. Once terminal operation is specified we can't use them again

3) Read a stream: All terminal operation which prints out elements are read operation - forEach(), collect()

4) Delete stream: 
  removeIf uses an Iterator to iterate over the list and match the elements using the predicate.
  removeAll uses a simple for-loop to remove all the matching elements in the list.

Non-Terminal Operations:
filter()
map()
flatMap()
distinct()
limit()
peek()
Terminal Operations:
anyMatch()
allMatch()
noneMatch()
collect()
count()
findAny()
findFirst()
forEach()
min()
max()
reduce()
toArray()

Java Streams

Why we need streams in java?

1) A stream does not store data. An operation on a stream does not modify its source, but simply produces a result. Without stream we need to have iterator or foreach to get item in collection.

2) You can add to or remove elements from collections. But, you can’t add to or remove elements from streams. Stream consumes a source, performs operations on it and returns a result.

3) Streams can be traversed only once. If you traverse the stream once, it is said to be consumed. To traverse it again, you have to get new stream from the source again. But, collections can be traversed multiple times.

List<Integer> numbers = Arrays.asList(4, 2, 8, 9, 5, 6, 7);
Stream<Integer> numbersGreaterThan5 = numbers.stream().filter(i -> i > 5);
         
//Traversing numbersGreaterThan5 stream first time
numbersGreaterThan5.forEach(System.out::println);
         
//Second time traversal will throw error  
//Error : stream has already been operated upon or closed   
numbersGreaterThan5.forEach(System.out::println);

4) Collections elements are computed at the beginning itself. But, streams are lazily constructed i.e intermediate operations are not evaluated until terminal operation is invoked.

//Here, not all numbers are evaluated.
//numbers are evaluated until 3 numbers >= 5 are found.
Arrays.asList(4, 2, 8, 9, 5, 6, 7).stream().filter(i -> i >= 5).limit(3).forEach(System.out::println);

Operations done by streams are classified as - filtering, matching and mapping