Functional Programming - What you need to know
Tags: programming, java, certification, tutorial
Published: 2022-05-13 15:50:04

# Functional Programming

## Introduction

* focusing on the Streams API.
* a functional interface has exactly one abstract method. 
  * java.util.function package.

| Functional interface |	Return type	| Method name |	# of parameters |  
|----------------------|----------------|-------------|-----------------|
| Supplier<T>          | T              | get()       | 0               | 
| Consumer<T>          | void           | accept(T)   | 1               |
| BiConsumer<T, U>     | void           | accept(T,U) | 2 (T, U)        |
| Predicate<T>         | boolean        | test(T)     | 1 (T)           |
| BiPredicate<T, U>    | boolean        | test(T,U)   | 2 (T, U)        |
| Function<T, R> 	   | R 	            | apply(T) 	  | 1 (T)           |
| BiFunction<T, U, R>  | R              | apply(T,U)  | 2 (T, U)        |
| UnaryOperator<T> 	   | T 	            | apply(T) 	  | 1 (T)           |
| BinaryOperator<T>    | T 	            | apply(T,T)  | 2 (T, T)        |

## IMPLEMENTING SUPPLIER

* A _Supplier_ is used when you want to generate or supply values without taking any input.
  *  is often used when constructing new objects.

```java
@FunctionalInterface
public interface Supplier<T> {
   T get();
}
```

```
Supplier<LocalDate> s1 = () -> LocalDate.now();
 
LocalDate d1 = s1.get();
 
System.out.println(d1);
```

## IMPLEMENTING CONSUMER AND BICONSUMER

* You use a _Consumer_ when you want to do something with a parameter but not return anything.
* _BiConsumer_ does the same thing except that it takes two parameters.

```java
@FunctionalInterface
public interface Consumer<T> {
   void accept(T t);
}
 
@FunctionalInterface
public interface BiConsumer<T, U> {
   void accept(T t, U u);
}
```

```
Consumer<String> c1 = x -> System.out.println(x);
 
c1.accept("Annie");
```

```
var map = new HashMap<String, Integer>();
BiConsumer<String, Integer> b1 = (k, v) -> map.put(k, v);
 
b1.accept("chicken", 7);
b1.accept("chick", 1);
 
System.out.println(map);
```

## IMPLEMENTING PREDICATE AND BIPREDICATE

* _Predicate_ is often used when filtering or matching. 
* A _BiPredicate_ is just like a Predicate except that it takes two parameters instead of one.

```
@FunctionalInterface
public interface Predicate<T> {
   boolean test(T t);
}
 
@FunctionalInterface
public interface BiPredicate<T, U> {
   boolean test(T t, U u);
}
```

```
Predicate<String> p1 = x -> x.isEmpty();
 
System.out.println(p1.test(""));  // true
```

```
BiPredicate<String, String> b1 = (string, prefix) -> string.startsWith(prefix);
 
System.out.println(b1.test("chicken", "chick"));  // true
```

## IMPLEMENTING FUNCTION AND BIFUNCTION

* A Function is responsible for turning one parameter into a value of a potentially different type and returning it.
* a BiFunction is responsible for turning two parameters into a value and returning it. 

```java
@FunctionalInterface
public interface Function<T, R> {
   R apply(T t);
}
 
@FunctionalInterface
public interface BiFunction<T, U, R> {
   R apply(T t, U u);
}
```

```
Function<String, Integer> f1 = x -> x.length();
 
System.out.println(f1.apply("cluck")); // 5
```

```
BiFunction<String, String, String> b1 = (string, toAdd) -> string.concat(toAdd);
 
System.out.println(b1.apply("baby ", "chick")); // baby chick
```

## IMPLEMENTING UNARYOPERATOR AND BINARYOPERATOR

* _UnaryOperator_ and _BinaryOperator_ require all type parameters to be the same type. 
* A _UnaryOperator_ transforms its value into one of the same type.
* A _BinaryOperator_ merges two values into one of the same type.

```java
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
  T apply(T t);
}
 
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T, T, T> {
  T apply(T t1, T t2);
}
```

```
UnaryOperator<String> u1 = x -> x.toUpperCase();
 
System.out.println(u1.apply("chirp"));  // CHIRP
```

```
BinaryOperator<String> b1 = (string, toAdd) -> string.concat(toAdd);
 
System.out.println(b1.apply("baby ", "chick")); // baby chick
```

## CONVENIENCE METHODS ON FUNCTIONAL INTERFACES

* Several of the common functional interfaces provide a number of helpful **_default_** methods.

|Interface instance	| Method return type | Method name | Method parameters |
|-------------------|--------------------|-------------|-------------------|
|Consumer           |Consumer            |andThen()    |Consumer           |
|Function           |Function            |andThen()    |Function           |
|Function           |Function            |compose()    |Function           |
|Predicate          |Predicate           |and()        |Predicate          |
|Predicate          |Predicate           |negate()     |—                  |
|Predicate          |Predicate           |or()         |Predicate          |

```
Predicate<String> egg = s -> s.contains("egg");
Predicate<String> brown = s -> s.contains("brown");

Predicate<String> brownEggs = egg.and(brown);
Predicate<String> otherEggs = egg.and(brown.negate());
```

```
Consumer<String> c1 = x -> System.out.print("1: " + x);
Consumer<String> c2 = x -> System.out.print(",2: " + x);
 
Consumer<String> combined = c1.andThen(c2);
combined.accept("Annie");              // 1: Annie,2: Annie
```

```
Function<Integer, Integer> before = x -> x + 1;
Function<Integer, Integer> after = x -> x * 2;
 
Function<Integer, Integer> combined = after.compose(before);
System.out.println(combined.apply(3));   // 8
```

## Optional

* An _Optional_ is created using a factory.
  * Think of an Optional as a box that might have something in it or might instead be empty

```
Optional.empty()
Optional.of(<value>)
```

### CREATING AN OPTIONAL

```
10: public static Optional<Double> average(int… scores) {
11:    if (scores.length == 0) return Optional.empty();
12:    int sum = 0;
13:    for (int score: scores) sum += score;
14:    return Optional.of((double) sum / scores.length);
15: }

...
        
      System.out.println(average(90, 100)); // Optional[95.0]
      System.out.println(average());        // Optional.empty

...
        
20: Optional<Double> opt = average(90, 100);
21: if (opt.isPresent())
22:    System.out.println(opt.get()); // 95.0
```

* We'd get an exception since there is no value inside the Optional.
  * _java.util.NoSuchElementException: No value present_

```
26: Optional<Double> opt = average();
27: System.out.println(opt.get()); // NoSuchElementException
```

* it is common to want to use empty() when the value is null.
  * You can do this with an if statement or ternary operator ( _? :_) .

```
Optional o = (value == null) ? Optional.empty() : Optional.of(value);
```

Or

```
Optional o = Optional.ofNullable(value);
```

| Method                  | When Optional is empty                       | When Optional contains a value |
|-------------------------|----------------------------------------------|--------------------------------|
| get()                   | Throws an exception                          | Returns value                  |
| ifPresent(Consumer c)   | Does nothing	                             | Calls Consumer with value      |
| isPresent()             |	Returns false	                             | Returns true                   |
| orElse(T other)         |	Returns other parameter                      | Returns value                  |
| orElseGet(Supplier s)   | Returns result of calling Supplier	         | Returns value                  |
| orElseThrow() 	      | Throws NoSuchElementException	             | Returns value                  |
| orElseThrow(Supplier s) | Throws exception created by calling Supplier | Returns value                  |

* _ifPresent()_ -> You can think of it as an _if_ statement with no _else_.

```
Optional<Double> opt = average(90, 100);
opt.ifPresent(System.out::println);
```

### DEALING WITH AN EMPTY OPTIONAL

```
30: Optional<Double> opt = average();
31: System.out.println(opt.orElse(Double.NaN));
32: System.out.println(opt.orElseGet(() -> Math.random()));
```
```
NaN
0.49775932295380165
```

```
30: Optional<Double> opt = average();
31: System.out.println(opt.orElseThrow());
```

* Without specifying a Supplier for the exception, Java will throw a _NoSuchElementException_.

```
Exception in thread "main" java.util.NoSuchElementException: 
   No value present
   at java.base/java.util.Optional.orElseThrow(Optional.java:382)
```

```
30: Optional<Double> opt = average();
31: System.out.println(opt.orElseThrow(
32:    () -> new IllegalStateException()));
```

```
Exception in thread "main" java.lang.IllegalStateException
   at optionals.Methods.lambda$orElse$1(Methods.java:30)
   at java.base/java.util.Optional.orElseThrow(Optional.java:408)
```

## Using Streams

* A stream in Java is a sequence of data.
* A stream pipeline consists of the operations that run on a stream to produce a result.

### UNDERSTANDING THE PIPELINE FLOW

* stream pipeline as an assembly line in a factory.
  * stream operations

* There are three parts to a stream pipeline
  * source: Where the stream comes from
  * intermediate operations: Transforms the stream into another one.
  * terminal operation: Actually produces a result. 

* The stream is no longer valid after a terminal operation completes.

### CREATING STREAM SOURCES

* In Java, the streams are represented by the Stream<T> interface, defined in the **_java.util.stream_** package.

### Creating Finite Streams

```
11: Stream<String> empty = Stream.empty();          // count = 0
12: Stream<Integer> singleElement = Stream.of(1);   // count = 1
13: Stream<Integer> fromArray = Stream.of(1, 2, 3); // count = 3
14: var list = List.of("a", "b", "c");
15: Stream<String> fromList = list.stream();
```

### Creating Infinite Streams

```
7: Stream<Double> randoms = Stream.generate(Math::random);
18: Stream<Integer> oddNumbers = Stream.iterate(1, n -> n + 2);
```

* overloaded example of _iterate()_

```
19: Stream<Integer> oddNumberUnder100 = Stream.iterate(
20:    1,                // seed
21:    n -> n < 100,     // Predicate to specify when done
22:    n -> n + 2);      // UnaryOperator to get next value
```

| Method                                         | Finite or infinite? | Notes |
|------------------------------------------------|---------------------|-------|
| Stream.empty() 	                             | Finite	           | Creates Stream with zero elements
| Stream.of(varargs) 	                         | Finite	           | Creates Stream with elements listed
| coll.stream() 	                             | Finite	           | Creates Stream from a Collection
| coll.parallelStream() 	                     | Finite              | Creates Stream from a Collection where the stream can run in parallel
| Stream.generate(supplier)                      | Infinite	           | Creates Stream by calling the Supplier for each element upon request
| Stream.iterate(seed, unaryOperator) 	         | Infinite	           | Creates Stream by using the seed for the first element and then calling the UnaryOperator for each subsequent element upon request
| Stream.iterate(seed, predicate, unaryOperator) | Finite or infinite  | Creates Stream by using the seed for the first element and then calling the UnaryOperator for each subsequent element upon request. Stops if the Predicate returns false

## USING COMMON TERMINAL OPERATIONS

* You can perform a terminal operation without any intermediate operations but not the other way around.
* _Reductions_ are a special type of terminal operation where all the contents of the stream are combined into a single primitive or Object.

| Method                                      | What happens for infinite streams | Return value | Reduction |
|---------------------------------------------|-----------------------------------|--------------|-----------|
| count()                                     | Does not terminate                | long         | Yes       |
| min() <br> max()                            | Does not terminate                | Optional<T>  | Yes       |
| findAny() <br> findFirst()                  | Terminates                        | Optional<T>  | No        |
| allMatch() <br> anyMatch() <br> noneMatch() | Sometimes terminates              | boolean      | No        |
| forEach()                                   | Does not terminate                | void         | No        |
| reduce()                                    | Does not terminate                | Varies       | Yes       |
| collect()                                   | Does not terminate                | Varies       | Yes       |

### count()

* The _count()_ method determines the number of elements in a finite stream.

```
long count()
```

```
Stream<String> s = Stream.of("monkey", "gorilla", "bonobo");
System.out.println(s.count());   // 3
```

### min() and max()

* The _min() and max()_ methods allow you to pass a custom comparator and find the smallest or largest value in a finite stream according to that sort order.

```
Optional<T> min(Comparator<? super T> comparator)
Optional<T> max(Comparator<? super T> comparator)
```

```
Stream<String> s = Stream.of("monkey", "ape", "bonobo");
Optional<String> min = s.min((s1, s2) -> s1.length()-s2.length());
min.ifPresent(System.out::println); // ape
```

* Returns an optional because we have cases where is not possible to find a min or max.

```
Optional<?> minEmpty = Stream.empty().min((s1, s2) -> 0);
System.out.println(minEmpty.isPresent()); // false
```

### findAny() and findFirst()

* The _findAny() and findFirst()_ methods return an element of the stream unless the stream is empty. 
  * If the stream is empty, they return an empty Optional.
* can terminate with an infinite stream.

```
Optional<T> findAny()
Optional<T> findFirst()
```

```
Stream<String> s = Stream.of("monkey", "gorilla", "bonobo");
Stream<String> infinite = Stream.generate(() -> "chimp");
 
s.findAny().ifPresent(System.out::println);        // monkey (usually)
infinite.findAny().ifPresent(System.out::println); // chimp
```

### allMatch(), anyMatch(), and noneMatch()

* The _allMatch()_, _anyMatch()_, and _noneMatch()_ methods search a stream and return information about how the stream pertains to the predicate.
* These may or may not terminate for infinite streams.
* they are not reductions because they do not necessarily look at all of the elements.

```
boolean anyMatch(Predicate <? super T> predicate)
boolean allMatch(Predicate <? super T> predicate)
boolean noneMatch(Predicate <? super T> predicate)
```

```
var list = List.of("monkey", "2", "chimp");
Stream<String> infinite = Stream.generate(() -> "chimp");
Predicate<String> pred = x -> Character.isLetter(x.charAt(0));
 
System.out.println(list.stream().anyMatch(pred));  // true
System.out.println(list.stream().allMatch(pred));  // false
System.out.println(list.stream().noneMatch(pred)); // false
System.out.println(infinite.anyMatch(pred));       // true
```

### forEach()

* it is common to iterate over the elements of a stream.
* on an infinite stream does not terminate.
* it is not a reduction.

```
void forEach(Consumer<? super T> action)
```

```
tream<String> s = Stream.of("Monkey", "Gorilla", "Bonobo");
s.forEach(System.out::print); // MonkeyGorillaBonobo
```

### reduce()

* The reduce() method combines a stream into a single object.

```
T reduce(T identity, BinaryOperator<T> accumulator)
 
Optional<T> reduce(BinaryOperator<T> accumulator)
 
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
```

* The _identity_ is the initial value of the reduction
* The accumulator combines the current result with the current value in the stream.

```
var array = new String[] { "w", "o", "l", "f" };
var result = "";
for (var s: array) result = result + s;
System.out.println(result); // wolf
```

```
Stream<String> stream = Stream.of("w", "o", "l", "f");
String word = stream.reduce("", (s, c) -> s + c);
System.out.println(word); // wolf
```

OR

```
Stream<String> stream = Stream.of("w", "o", "l", "f");
String word = stream.reduce("", String::concat);
System.out.println(word); // wolf
```

```
Stream<Integer> stream = Stream.of(3, 5, 6);
System.out.println(stream.reduce(1, (a, b) -> a*b));  // 90
```

* There are three choices for what is in the Optional. 
  * If the stream is empty, an empty Optional is returned.
  * If the stream has one element, it is returned.
  * If the stream has multiple elements, the accumulator is applied to combine them.

```
BinaryOperator<Integer> op = (a, b) -> a * b;
Stream<Integer> empty = Stream.empty();
Stream<Integer> oneElement = Stream.of(3);
Stream<Integer> threeElements = Stream.of(3, 5, 6);
 
empty.reduce(op).ifPresent(System.out::println);         // no output
oneElement.reduce(op).ifPresent(System.out::println);    // 3
threeElements.reduce(op).ifPresent(System.out::println); // 90
```

* The third method signature is used when we are dealing with different types.

```
Stream<String> stream = Stream.of("w", "o", "l", "f!");
int length = stream.reduce(0, (i, s) -> i+s.length(), (a, b) -> a+b);
System.out.println(length); // 5
```

* _i_ is Integer and _s_ is a String.
* _combiner_ used in parallel streams.

### collect()

* is a special type of reduction called a _mutable reduction_.

```
<R> R collect(Supplier<R> supplier, 
   BiConsumer<R, ? super T> accumulator, 
   BiConsumer<R, R> combiner)
 
<R,A> R collect(Collector<? super T, A,R> collector)
```

```
Stream<String> stream = Stream.of("w", "o", "l", "f");
 
StringBuilder word = stream.collect(
   StringBuilder::new,
   StringBuilder::append, 
   StringBuilder::append)
 
System.out.println(word); // wolf
```

* _Combiner_ used in parallel streams;

```
Stream<String> stream = Stream.of("w", "o", "l", "f");
 
TreeSet<String> set = stream.collect(
   TreeSet::new, 
   TreeSet::add,
   TreeSet::addAll);
 
System.out.println(set); // [f, l, o, w]
```

```
Stream<String> stream = Stream.of("w", "o", "l", "f");
TreeSet<String> set = 
   stream.collect(Collectors.toCollection(TreeSet::new));
System.out.println(set); // [f, l, o, w]
```

```
Stream<String> stream = Stream.of("w", "o", "l", "f");
Set<String> set = stream.collect(Collectors.toSet());
System.out.println(set); // [f, w, l, o]
```

## USING COMMON INTERMEDIATE OPERATIONS

* an intermediate operation produces a stream as its result.

### filter()

* The _filter()_ method returns a Stream with elements that match a given expression.

```
Stream<T> filter(Predicate<? super T> predicate)
```

```
Stream<String> s = Stream.of("monkey", "gorilla", "bonobo");
s.filter(x -> x.startsWith("m"))
   .forEach(System.out::print); // monkey
```

### distinct()

* The _distinct()_ method returns a stream with duplicate values removed.

```
Stream<T> distinct()
```

```
Stream<String> s = Stream.of("duck", "duck", "duck", "goose");
s.distinct()
   .forEach(System.out::print); // duckgoose
```

### limit() and skip()

* The _limit()_ and _skip()_ methods can make a Stream smaller, or they could make a finite stream out of an infinite stream.

```
Stream<T> limit(long maxSize)
Stream<T> skip(long n)
```

```
Stream<Integer> s = Stream.iterate(1, n -> n + 1);
s.skip(5)
   .limit(2)
   .forEach(System.out::print); // 67
```

### map()

* The map() method creates a one‐to‐one mapping from the elements in the stream to the elements of the next step in the stream.

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

```
Stream<String> s = Stream.of("monkey", "gorilla", "bonobo");
s.map(String::length)
   .forEach(System.out::print); // 676
```

### flatMap()

* The flatMap() method takes each element in the stream and makes any elements it contains top‐level elements in a single stream.

```
<R> Stream<R> flatMap(
   Function<? super T, ? extends Stream<? extends R>> mapper)
```

```
List<String> zero = List.of();
var one = List.of("Bonobo");
var two = List.of("Mama Gorilla", "Baby Gorilla");
Stream<List<String>> animals = Stream.of(zero, one, two);
 
animals.flatMap(m -> m.stream())
   .forEach(System.out::println);
```

```
Bonobo
Mama Gorilla
Baby Gorilla
```

### sorted()

* The sorted() method returns a stream with the elements sorted.

```
Stream<T> sorted()
Stream<T> sorted(Comparator<? super T> comparator)
```

```
Stream<String> s = Stream.of("brown-", "bear-");
s.sorted()
   .forEach(System.out::print); // bear-brown
```

```
Stream<String> s = Stream.of("brown bear-", "grizzly-");
s.sorted(Comparator.reverseOrder())
   .forEach(System.out::print); // grizzly-brown bear-
```

### peek()

* it allows us to perform a stream operation without actually changing the stream.

```
Stream<T> peek(Consumer<? super T> action)
```

```
var stream = Stream.of("black bear", "brown bear", "grizzly");
long count = stream.filter(s -> s.startsWith("g"))
   .peek(System.out::println).count();              // grizzly
System.out.println(count);                          // 1
```

### PUTTING TOGETHER THE PIPELINE

```
var list = List.of("Toby", "Anna", "Leroy", "Alex");
list.stream()
   .filter(n -> n.length() == 4)
   .sorted()
   .limit(2)
   .forEach(System.out::println); 
```

```
Stream.generate(() -> "Elsa")
   .filter(n -> n.length() == 4)
   .limit(2)
   .sorted()
   .forEach(System.out::println);
```

## Working with Primitive Streams

### CREATING PRIMITIVE STREAMS

* _IntStream_: Used for the primitive types int, short, byte, and char
* _LongStream_: Used for the primitive type long
* _DoubleStream_: Used for the primitive types double and float

| Method                   | Primitive stream	                         | Description                                                                 |
|--------------------------|---------------------------------------------|-------------|
| OptionalDouble average() | IntStream <br> LongStream <br> DoubleStream | The arithmetic mean of the elements |
| Stream\<T\> boxed()        | IntStream <br> LongStream <br> DoubleStream | A Stream<T> where T is the wrapper class associated with the primitive value|
| OptionalInt max() <br> OptionalLong max() <br>  OptionalDouble max() | IntStream <br> LongStream <br> DoubleStream | The maximum element of the stream |
| OptionalInt min() <br> OptionalLong min() <br>  OptionalDouble min() | IntStream <br> LongStream <br> DoubleStream | The minimum element of the stream |
| IntStream range(int a, int b) <br> LongStream range(long a, long b) | IntStream <br> LongStream | Returns a primitive stream from a (inclusive) to b (exclusive) |
| IntStream rangeClosed(int a, int b) <br> LongStream rangeClosed(long a, long b) | IntStream <br> LongStream | Returns a primitive stream from a (inclusive) to b (inclusive) |
| int sum() <br> long sum() <br> double sum() | IntStream <br> LongStream <br> DoubleStream | Returns the sum of the elements in the stream |
| IntSummaryStatistics summaryStatistics() <br> LongSummaryStatistics summaryStatistics() <br> DoubleSummaryStatistics summaryStatistics() | IntStream <br> LongStream <br> DoubleStream | Returns an object containing numerous stream statistics such as the average, min, max, etc. |  

<p style="text-align: center;"> Table - Common primitive stream methods </p>

* Examples:

* Creating streams
```
DoubleStream empty = DoubleStream.empty();

DoubleStream oneValue = DoubleStream.of(3.14);
oneValue.forEach(System.out::println);
 
DoubleStream varargs = DoubleStream.of(1.0, 1.1, 1.2);
varargs.forEach(System.out::println);
```

```
3.14
1.0
1.1
1.2
```

* Creating infinite streams
```
var random = DoubleStream.generate(Math::random);
var fractions = DoubleStream.iterate(.5, d -> d / 2);
random.limit(3).forEach(System.out::println);
fractions.limit(3).forEach(System.out::println);
```

```
0.07890654781186413
0.28564363465842346
0.6311403511266134
0.5
0.25
0.125
```

* Using range
```
IntStream range = IntStream.range(1, 6);
range.forEach(System.out::println);
```

```
IntStream rangeClosed = IntStream.rangeClosed(1, 5);
rangeClosed.forEach(System.out::println);
```

### MAPPING STREAMS

* Another way to create a primitive stream is by mapping from another stream type.

| Source stream class | To create Stream | To create DoubleStream | To create IntStream | To create LongStream |
|---------------------|------------------|------------------------|---------------------|----------------------|
| Stream<T> | map() | mapToDouble() | mapToInt() | mapToLong() |
| DoubleStream| mapToObj()| map()| mapToInt()| mapToLong()|
| IntStream| mapToObj()| mapToDouble()| map()| mapToLong()|
| LongStream | mapToObj()| mapToDouble()| mapToInt()| map()|

<p align="center">Mapping methods between types of streams</p>

* example

```
Stream<String> objStream = Stream.of("penguin", "fish");
IntStream intStream = objStream.mapToInt(s -> s.length());
```

| Source stream class | To create Stream | To create DoubleStream | To create IntStream | To create LongStream |
|---------------------|-----------------|-------------------------|---------------------|---------|
|Stream<T>|Function<T,R>|ToDoubleFunction<T>|ToIntFunction<T>|ToLongFunction<T>|
|DoubleStream|Double Function<R>|DoubleUnary Operator|DoubleToInt Function |DoubleToLong Function|
|IntStream |IntFunction<R> |IntToDouble Function |IntUnary Operator |IntToLong Function|
|LongStream | Long Function<R>| LongToDouble Function| LongToInt Function |LongUnary Operator|

<p align="center">Function parameters when mapping between types of streams</p>


* flatmap() on primitive streams has a different method name

```
var integerList = new ArrayList<Integer>();
IntStream ints = integerList.stream()
.flatMapToInt(x -> IntStream.of(x));
DoubleStream doubles = integerList.stream()
.flatMapToDouble(x -> DoubleStream.of(x));
LongStream longs = integerList.stream()
.flatMapToLong(x -> LongStream.of(x));
```

### USING OPTIONAL WITH PRIMITIVE STREAMS

```
var stream = IntStream.rangeClosed(1,10);
OptionalDouble optional = stream.average();

optional.ifPresent(System.out::println);                  // 5.5
System.out.println(optional.getAsDouble());               // 5.5
System.out.println(optional.orElseGet(() -> Double.NaN)); // 5.5
```

* _OptionalDouble_ is for a primitive and _Optional<Double>_ is for the Double wrapper class.
* getAsDouble() rather than get().
* orElseGet() takes a DoubleSupplier instead of a Supplier.

|  | OptionalDouble | OptionalInt | OptionalLong |
|-----|----------------|-----------|------------|
|Getting as a primitive|getAsDouble()|getAsInt()|getAsLong()|
|orElseGet() parameter type|DoubleSupplier|IntSupplier|LongSupplier|
|Return type of max() and min()|OptionalDouble|OptionalInt|OptionalLong|
|Return type of sum()|double|int|long|
|Return type of average()|OptionalDouble|OptionalDouble|OptionalDouble|

<p align="center">Function parameters when mapping between types of streams</p>

* The sum() method does not return an optional.
* The min() method returns an OptionalDouble.
```
5: LongStream longs = LongStream.of(5, 10);
6: long sum = longs.sum();
7: System.out.println(sum);     // 15
8: DoubleStream doubles = DoubleStream.generate(() -> Math.PI);
9: OptionalDouble min = doubles.min(); // runs infinitely
```

### SUMMARIZING STATISTICS

```
private static int range(IntStream ints) {
   IntSummaryStatistics stats = ints.summaryStatistics();
   if (stats.getCount() == 0) throw new RuntimeException();
   return stats.getMax()-stats.getMin();
}
```

* Summary statistics include the following:
  * Smallest number (minimum): getMin()
  * Largest number (maximum): getMax()
  * Average: getAverage()
  * Sum: getSum()
  * Number of values: getCount()

## LEARNING THE FUNCTIONAL INTERFACES FOR PRIMITIVES

### Functional Interfaces for boolean

* BooleanSupplier
```
boolean getAsBoolean()
```

```
12: BooleanSupplier b1 = () -> true;
13: BooleanSupplier b2 = () -> Math.random()> .5;
14: System.out.println(b1.getAsBoolean());  // true
15: System.out.println(b2.getAsBoolean());  // false
```

### Functional Interfaces for double, int, and long

<p align="center">Common functional interfaces for primitives</p>

|Functional interfaces|	# parameters | Return type | Single abstract method |
|---------------------|--------------|-------------|------------------------|
|DoubleSupplier <br> IntSupplier <br> LongSupplier|	0| double <br> int <br> long | getAsDouble <br> getAsInt <br> getAsLong |
|DoubleConsumer <br> IntConsumer <br> LongConsumer| 1 (double) <br> 1 (int) <br> 1 (long) | void | accept |
|DoublePredicate <br> IntPredicate <br> LongPredicate| 1 (double) <br> 1 (int) <br> 1 (long) | boolean | test |
|DoubleFunction\<R\> <br> IntFunction\<R\> LongFunction\<R\>|1 (double) <br> 1 (int) <br> 1 (long) | R | apply| 
|DoubleUnaryOperator <br> IntUnaryOperator <br> LongUnaryOperator|1 (double) <br> 1(int) <br> 1(long) | double <br> int <br> long | applyAsDouble <br> applyAsInt <br> applyAsLong |
|DoubleBinaryOperator <br> IntBinaryOperator <br> LongBinaryOperator| 2 (double, double) <br> 2 (int, int) <br> 2 (long, long) | double <br> int <br> long | applyAsDouble <br> applyAsInt <br> applyAsLong|

<p align="center">Primitive‐specific functional interfaces</p>

|Functional interfaces	|# parameters	|Return type	|Single abstract method|
|-----------------------|---------------|---------------|----------------------|
|ToDoubleFunction<T> <br> ToIntFunction<T> <br> ToLongFunction<T> <br>| 1 (T) | double <br> int <br> long | applyAsDouble <br> applyAsInt <br> applyAsLong |
|ToDoubleBiFunction<T, U>  <br> ToIntBiFunction<T, U>  <br> ToLongBiFunction<T, U>| 2 (T, U) | double <br> int <br> long | applyAsDouble <br> applyAsInt <br> applyAsLong|
|DoubleToIntFunction <br> DoubleToLongFunction <br> IntToDoubleFunction <br> IntToLongFunction <br> LongToDoubleFunction <br> LongToIntFunction | 1 (double) <br> 1 (double) <br> 1 (int) <br> 1 (int) <br> 1 (long)  <br>1 (long)| int  <br> long <br> double <br> long <br> double <br> int |
applyAsInt <br> applyAsLong <br> applyAsDouble <br> applyAsLong <br> applyAsDouble <br> applyAsInt|
|ObjDoubleConsumer<T> <br> ObjIntConsumer<T> <br> ObjLongConsumer<T>|2 (T, double) <br> 2 (T, int) <br> 2 (T, long) | void | accept |

## COLLECTING RESULTS

* There are many predefined collectors
  * These collectors are available via _static_ methods on the _Collectors_ interface.

<p align="center">Examples of grouping/partitioning collectors</p>

|Collector | Description | Return value when passed to collect |
|----------|-------------|-------------------------------------|
|averagingDouble(ToDoubleFunction f) <br> averagingInt(ToIntFunction f) <br> averagingLong(ToLongFunction f) |Calculates the average for our three core primitive types |Double|
|counting() |Counts the number of elements |Long|
|groupingBy(Function f) <br> groupingBy(Function f, Collector dc) <br> groupingBy(Function f, Supplier s, Collector dc) 	|Creates a map grouping by the specified function with the optional map type supplier and optional downstream collector |Map<K, List<T>>|
|joining(CharSequence cs) 	|Creates a single String using cs as a delimiter between elements if one is specified |String|
|maxBy(Comparator c) <br> minBy(Comparator c) |Finds the largest/smallest elements |Optional<T>|
|mapping(Function f, Collector dc) | Adds another level of collectors |Collector|
|partitioningBy(Predicate p) <br> partitioningBy(Predicate p, Collector dc) |Creates a map grouping by the specified predicate with the optional further downstream collector |Map<Boolean, List<T>>|
|summarizingDouble(ToDoubleFunction f) <br> summarizingInt(ToIntFunction f) <br> summarizingLong(ToLongFunction f) |	Calculates average, min, max, and so on |DoubleSummaryStatistics IntSummaryStatistics LongSummaryStatistics|
|summingDouble(ToDoubleFunction f) <br> summingInt(ToIntFunction f) <br> summingLong(ToLongFunction f) |Calculates the sum for our three core primitive types |Double <br> Integer <br> Long|
|toList() <br> toSet() |Creates an arbitrary type of list or set |List <br> Set|
|toCollection(Supplier s) |Creates a Collection of the specified type |Collection|
|toMap(Function k, Function v) <br> toMap(Function k, Function v, BinaryOperator m) <br> toMap(Function k, Function v, BinaryOperator m, Supplier s) |Creates a map using functions to map the keys, values, an optional merge function, and an optional map type supplier |Map|

### Collecting Using Basic Collectors

```
var ohMy = Stream.of("lions", "tigers", "bears");
String result = ohMy.collect(Collectors.joining(", "));
System.out.println(result); // lions, tigers, bears
```

```
var ohMy = Stream.of("lions", "tigers", "bears");
Double result = ohMy.collect(Collectors.averagingInt(String::length));
System.out.println(result); // 5.333333333333333
```

```
var ohMy = Stream.of("lions", "tigers", "bears");
TreeSet<String> result = ohMy
   .filter(s -> s.startsWith("t"))
   .collect(Collectors.toCollection(TreeSet::new));
System.out.println(result); // [tigers]
```

### Collecting into Maps

* you need to specify two functions.
  * The first function tells the collector how to create the key.
  * The second function tells the collector how to create the value.

```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<String, Integer> map = ohMy.collect(
   Collectors.toMap(s -> s, String::length));
System.out.println(map); // {lions=5, bears=5, tigers=6}
```

* In case of store the same key two times, the collector has no idea what to do, it “solves” the problem by throwing an exception and making it our problem.

```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<Integer, String> map = ohMy.collect(Collectors.toMap(
   String::length, 
   k -> k)); // BAD
```

```
Exception in thread "main" 
   java.lang.IllegalStateException: Duplicate key 5
```

* we can

```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<Integer, String> map = ohMy.collect(Collectors.toMap(
   String::length,
   k -> k, 
  (s1, s2) -> s1 + "," + s2));
System.out.println(map);            // {5=lions,bears, 6=tigers}
System.out.println(map.getClass()); // class java.util.HashMap
```

* if you want to save in a TreeMap

```
var ohMy = Stream.of("lions", "tigers", "bears");
TreeMap<Integer, String> map = ohMy.collect(Collectors.toMap(
   String::length, 
   k -> k, 
   (s1, s2) -> s1 + "," + s2,
   TreeMap::new));
System.out.println(map); //         // {5=lions,bears, 6=tigers}
System.out.println(map.getClass()); // class java.util.TreeMap
```

### Collecting Using Grouping, Partitioning, and Mapping

* suppose that we want to get groups of names by their length.

```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<Integer, List<String>> map = ohMy.collect(
   Collectors.groupingBy(String::length));
System.out.println(map);    // {5=[lions, bears], 6=[tigers]}
```

* Note that the function you call in groupingBy() cannot return null. It does not allow null keys.

* Suppose that we don't want a List as the value in the map and prefer a Set instead.

```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<Integer, Set<String>> map = ohMy.collect(
   Collectors.groupingBy(
      String::length, 
      Collectors.toSet()));
System.out.println(map);    // {5=[lions, bears], 6=[tigers]}
```

* We can even change the type of Map returned through yet another parameter.
```
var ohMy = Stream.of("lions", "tigers", "bears");
TreeMap<Integer, Set<String>> map = ohMy.collect(
   Collectors.groupingBy(
      String::length, 
      TreeMap::new, 
      Collectors.toSet()));
System.out.println(map); // {5=[lions, bears], 6=[tigers]}
```

* other option

```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<Integer, Long> map = ohMy.collect(
   Collectors.groupingBy(
      String::length, 
      Collectors.counting()));
System.out.println(map);    // {5=2, 6=1}
```

* Suppose that we wanted to get the first letter of the first animal alphabetically of each length.
  * _mapping()_ collector that lets us go down a level and add another collector.
    * takes two parameters: the function for the value and how to group it further.

```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<Integer, Optional<Character>> map = ohMy.collect(
   Collectors.groupingBy(
      String::length,
      Collectors.mapping(
         s -> s.charAt(0), 
         Collectors.minBy((a, b) -> a -b))));
System.out.println(map);    // {5=Optional[b], 6=Optional[t]}
```

* Partitioning is a special case of grouping.
  * two possible groups—true and false.
  * unlike groupingBy(), we cannot change the type of Map that gets returned.

```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<Boolean, List<String>> map = ohMy.collect(
   Collectors.partitioningBy(s -> s.length() <= 5));
System.out.println(map);    // {false=[tigers], true=[lions, bears]}
```

* modifying from _List_ to _Set_
```
var ohMy = Stream.of("lions", "tigers", "bears");
Map<Boolean, Set<String>> map = ohMy.collect(
   Collectors.partitioningBy(
      s -> s.length() <= 7, 
      Collectors.toSet()));
System.out.println(map);    // {false=[], true=[lions, tigers, bears]}
```

## REFERENCES

[Blog Java Streams ](https://medium.com/@thiagomedeiros_58357/java-streams-as-collections-melhoradas-4ccb436646c3)