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

Sunday, 24 May 2020

Comparator and Comparable interface:



Java String class implements the Comparable interface, you can sort them in their natural order, using the Collections sort() method.

if you want to sort the user defined objects in another order than their compare() implementation, then you need to use a Comparator implementation (java.util.Comparator)

Usual Implementation in Java 7:

import java.util.*;
public class Main
{
public static void main(String[] args) {
    List<Car> list = new ArrayList<>();

            list.add(new Car("Volvo V40" , "XYZ 201845", 5));
            list.add(new Car("Citroen C1", "ABC 164521", 4));
                ist.add(new Car("Dodge Ram" , "KLM 845990", 2));
Collections.sort(list, new BrandCompare());
for(Car lst: list){
    System.out.println(lst.brand);
}
}
}
class Car{
    public String brand;
    public String numberPlate;
    public int noOfDoors;

    public Car(String brand, String numberPlate, int noOfDoors) {
        this.brand = brand;
        this.numberPlate = numberPlate;
        this.noOfDoors = noOfDoors;
    }
}
class BrandCompare implements Comparator<Car>{
    
    public int compare(Car car1, Car car2) {
        return car1.brand.compareTo(car2.brand);
    }
}
class PlateCompare implements Comparator<Car>{
    
    public int compare(Car car1, Car car2) {
        return car1.numberPlate.compareTo(car2.numberPlate);
    }
}

Implementation in external class:

List<Car> list = new ArrayList<>();

list.add(new Car("Volvo V40" , "XYZ 201845", 5));
list.add(new Car("Citroen C1", "ABC 164521", 4));
list.add(new Car("Dodge Ram" , "KLM 845990", 2));

Comparator<Car> carBrandComparator = new Comparator<Car>() {
    @Override
    public int compare(Car car1, Car car2) {
        return car1.brand.compareTo(car2.brand);
    }
};

Collections.sort(list, carBrandComparator);


Implementation in external class using lambda experession:

List<Car> list = new ArrayList<>();

list.add(new Car("Volvo V40" , "XYZ 201845", 5));
list.add(new Car("Citroen C1", "ABC 164521", 4));
list.add(new Car("Dodge Ram" , "KLM 845990", 2));


Comparator<Car> carBrandComparatorLambda      =
    (car1, car2) -> car1.brand.compareTo(car2.brand);

Comparator<Car> carNumberPlatComparatorLambda =
    (car1, car2) -> car1.numberPlate.compareTo(car2.numberPlate);

Comparator<Car> carNoOfDoorsComparatorLambda  =
    (car1, car2) -> car1.noOfDoors - car2.noOfDoors;

Collections.sort(list, carBrandComparatorLambda);
Collections.sort(list, carNumberPlatComparatorLambda);
Collections.sort(list, carNoOfDoorsComparatorLambda);

We can also make use of getter method in bean to specify comparator field:

class Car{
    public String brand;
    public String numberPlate;
    public int noOfDoors;
    
    public String getBrand(){
        return this.brand;
    }
}

List<Car> list = new ArrayList<>();

list.add(new Car("Volvo V40" , "XYZ 201845", 5));
list.add(new Car("Citroen C1", "ABC 164521", 4));
list.add(new Car("Dodge Ram" , "KLM 845990", 2));

Comparator<Car> brandCompare =Comparator.comparing(Car::getBrand);  
 Collections.sort(al,brandCompare); 

Thursday, 21 May 2020

Running time of Java Iterator


Happy to see you after a long time. I have started working on Java collections and I have tried measuring retrieval time for each collection iterator types using 1000 records in Java 8 and found below time(in seconds) for iteration.

 List Name\Iterator types Iterator    Foreach For loop
 Array List 0.0856     0.044 0.0491
 Linked List 0.02867 0.0504 0.0467
 Vector 0.0342 0.0444 0.0584
 
I have also tried to use for-each lambda expression, this gives below time for each list collection:

Vector - 0.17455
Linked List - 0.17691
Array List - 0.18599

Retrieval time for binarySearch() in collection:

Vector - 0.00377
Linked List - 0.00377
Array List - 0.01069