Streams
1) Main methods:
- Filter
- Map
- forEach (Terminal)
- generate
- iterate
- flat map
- count (Terminal)
- reduce (Terminal)
2) Terminal Operations
Operations that once used, the stream cannot be used again
The use Eager evaluation.
(Read more)
3) Stateful Operation
The operation that maintain state to accomplish the operation
E.g
- distinct
- sorted
- limit
- count
When the operation on a particular variable in the stream relies on the previous operation of the previous variable
Uses lazy evaluation
For example,
in Count, the number of variables depends on how many variables were counted before the current variables
Stateful Operation is best avoided during parallelism
4) Stateless Operation
The opposite of stateful. They do not store any state across passes.
e.g
-filter
-map
-findAny
Is useful in parallel
5) Method references
When using methods such as forEach or sort, we can use method signatures for comparison.
e.g When wanting to print out values using for each
IntStream.forEach(System.out::println)
Another example:
int max = marksList.stream().map(x-> x.getMark()).max(Integer::compare).get();
//marksList is a list of marks
//getMark() is a non-static function in the class Mark
//marksList is a list of marks
//getMark() is a non-static function in the class Mark
There are four kinds of method references:
Kind | Example |
---|---|
Reference to a static method | ContainingClass::staticMethodName |
Reference to an instance method of a particular object | containingObject::instanceMethodName |
Reference to an instance method of an arbitrary object of a particular type | ContainingType::methodName |
6) Lambda
All local variables referenced from a lambda expression must be effectively final or final.
Meaning that the value of variable or parameter is never changed after initialisation
- public static void Main(String[] args){
- int count =0;
- IntStream.range(0,10)
- .forEach(x->{
- count++; //this is illegal
- })
- }
A Cheat to counter:
Assume we cannot use the .count() method
- public static void Main(String[] args){
- IntStream.range(0,10)
- .map(x->1)
- .reduce(0,(x,y) -> x+ y;);
- }
7) Peek()
Useful for parallel, they do not respect encounter order and are mostly used for debugging purposes
It acts like a for each but is not a terminal operation
8) Generate
Generate uses get() method from the supplier and returns supplier.get()
9) Iterate
Similar to generate excepts requires a seed.
Uses a function instead of a supplier to generate the method
It acts like a for each but is not a terminal operation
public class PeekExample { public static void main (String[] args) { IntStream.range(0, 5).parallel().peek(System.out::println). count(); } }
8) Generate
Generate uses get() method from the supplier and returns supplier.get()
9) Iterate
Similar to generate excepts requires a seed.
Uses a function instead of a supplier to generate the method
Functional Interfaces
1) Consumer
Has a single method
void accept(T t)
Example usage:
Has a single method
void accept(T t)
Example usage:
Note: Streams do not like to have state changes especially forEach
- void doSomething(Consumer<?> super Circle> action, Circle... circles{
- for(Circle c: circles){
- action.accept(circle); //perform the consumer on circle
- }
- }
Consumer can be a lambda or a method signature
2) Supplier
T get()
Supplies and generates the next variable.
T get()
Supplies and generates the next variable.
Example use
- List<Circle> getCircles(Supplier<Circle> supplier,int n ){
- List<Circle> outList = new ArrayList<>();
- for(int i=0;i<n;i++){
- outList.add(supplier.get());
- }
- return outList
- }
The supplier can be a lambda for example
()-> new Circle(Math.random());
()-> new Circle(Math.random());
3) UnaryOperator
R apply(T t)
Accepts a type T argument and produces a type R result
Example use:
<Prev Runtime compile time Next Enum>
R apply(T t)
Accepts a type T argument and produces a type R result
Example use:
- Stream.of(circles)
- .map( new Function<Circle,Double>(){
- @Override //annon class override
- public Double apply(Circle c){
- return c.getArea(); //inside circle class
- })
- .forEach(System.out::println);
Here we using an annon class to override the apply method, appling to each circle input and returning the area of each circle.
Function with multiple Arguments:
Using the one input apply method, can we make it such that it takes in two inputs?
We can use g.apply(1).apply(2) to call this method
4) Bi-function/ BinaryOperator
Similar to unary but instead takes in two inputs and produce an outpit
R apply(T t, U u )
More: https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
Function with multiple Arguments:
Using the one input apply method, can we make it such that it takes in two inputs?
- Function <Integer,UnaryOPerator<Integer>> g = new Function<>(){
- UnaryOperator<Integer> f = new UnaryOperator<>(){
- @Override
- public Integer apply(Integer y){
- return x+y;
- }
- };
- return f;
- };
- }
4) Bi-function/ BinaryOperator
Similar to unary but instead takes in two inputs and produce an outpit
R apply(T t, U u )
More: https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
Order of Operation
Given the example:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List<Integer> twoEvenSquares = numbers.stream().filter(n -> {
System.out.println("filtering " + n);
return n % 2 == 0;
}).map(n -> {
System.out.println("mapping " + n);
return n * n;
}).limit(2).collect(Collectors.toList());
- The only terminal operation
collect()
starts the evaluation of the chain. limit()
starts the evaluation of its ancestormap()
starts the evaluation of its ancestorfilter()
starts consuming values from the source stream1
is evaluated,2
is evaluated and the first value is producedmap()
consumes the first value returned by its ancestor and produce a value toolimit()
consume that valuecollect()
collect the first valuelimit()
requires another value from themap()
sourcemap()
requires another value from it's ancestorfilter()
resume the evaluation to produce another result and after evaluating3
and4
produce the new value4
map()
consumes it and produce a new valuelimit()
consume the new value and returns itcollect()
collects the last value.
<Prev Runtime compile time Next Enum>
No comments:
Post a Comment