Streams
# Imperative VS Functional Programming

Imperative: specify how something should be done
Declarative: express logic in terms of what needs to be done (SQL)
Functional programming is a special type of declarative programming
Streaming allow us to process a collection of data in a declarative/functional way
stream()returns a sequence of object- It doesn't store data
- It is just a way to get data out of the collection
- e.g. get water out of water tank

public class Movie implements Comparable<Movie> {
private String title;
private int likes;
}
public class StreamsDemo {
public static void show() {
List<Movie> movies = List.of(
new Movie("a", 10),
new Movie("b", 15),
new Movie("c", 20),
);
// imperative programming (a programming paradigm or a style of programming)
// there are statements that specify how something should be done
int count = 0;
for (var movie : movies) {
if (movie.getLikes() > 10)
count++;
}
// declarative programming (more functional because there are many functions)
var count2 = movies.stream()
.filter(movie -> movie.getLikes() > 10);
.count();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Creating a Stream
- From
- Collections
- arrays
- An arbitrary number of objects
- Infinite/finite streams
public class CreatingStreamsDemo {
public static void show() {
// From a collection
List<Integer> list = new ArrayList<>();
list.stream()
.forEach(n -> System.out.println(n));
// From an array
int[] array = { 1, 2, 3 };
Arrays.stream(array)
.forEach(n -> System.out.println(n));
// From an arbitrary number of objects
Stream.of(1, 2, 3)
.forEach(n -> System.out.println(n));
// Generate from scratch; infinite stream, not gonna crash
// Math.random() is called everytime
Stream.generate(() -> Math.random())
.limit(3) // if not limited, it will run forever
.forEach(n -> System.out.println(n));
// Generate from scratch
Stream.iterate(1, n -> n + 1) // start with 1, increment
.limit(10)
.forEach(n -> System.out.println(n));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Mapping Elements
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("b", 15),
new Movie("c", 20),
);
movies.stream()
.map(movie -> movie.getTitle())
.forEach(name -> System.out.println(name)); // apply to every movie
// Stream<List<x>> -> Stream<x>
var stream = Stream.of(List.of(1,2,3), List.of(4,5,6));
stream.flatMap(list -> list.stream())
.forEach(n -> System.out.println(name))
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Filtering Elements
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("b", 15),
new Movie("c", 20),
);
var filtered = movies.stream()
.filter(movie -> movie.getLikes() > 10)
.forEach(System.out.println(moive.getTitle));
Predicate<Movie> isPopular = m -> m.getLikes() > 10;
var filtered = movies.stream()
.filter(isPopular)
.forEach(System.out.println(moive.getTitle));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- Two types of operation of stream methods
- Intermediate
- map
- filter
- They return a new stream, so that data can be continuously transferred
- Terminal
- forEach
- Consume a stream
- If not called, nothing is done
- So it is very important to include terminal operation to the stream methods
- Intermediate
# Slicing Streams
limit(n)limit the number of elements in a streamskip(n)skip a number of elements in a streamtakeWhile(predicate)as long as the predicate is true, keep taking the elementsfilter(predicate)will loop over the the entire stream
dropWhile(predicate)
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("b", 15),
new Movie("c", 20),
);
// pagination for third page
movies.stream()
.skip(20) // skip the 20 items in the first two page
.limit(10) // show 10 items in the third page
.forEach(m - Sout(m));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Sorting Streams
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("c", 15),
new Movie("b", 20),
);
movies.stream()
.sorted((a, b) -> a.getTitle().compareTo(b.getTitle()));
.forEach(m -> System.out.println(m.getTitle()));
// simplify
movies.stream()
.sorted(Comparator.comparing(m -> m.getTitle()));
.forEach(m -> System.out.println(m.getTitle()));
// simplify
movies.stream()
.sorted(Comparator.comparing(Movie :: getTitle));
.forEach(m -> System.out.println(m.getTitle()));
// descending order
movies.stream()
.sorted(Comparator.comparing(Movie :: getTitle).reversed());
.forEach(m -> System.out.println(m.getTitle()));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Getting Unique Elements
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("a", 10),
new Movie("c", 15),
new Movie("b", 20),
);
movies.stream()
.map(Movie::getLikes)
.distinct() // return distinct values
.forEach(System.out::println);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Peeking Elements
- Add more messages
- Good for troubleshooting problems
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("c", 15),
new Movie("b", 20),
);
movies.stream()
.filte(m -> m.getLikes() > 10)
.peek(m -> System.out.println("filtered: " + m.getTitle())) // intermediate operation
.map(Movie::getTitle) // matching each movie to its title
.peek(t -> System.out.println("mapped: " + t))
.forEach(System.out::println); // terminal operation and returns void
// cannot call other operations after `forEach`
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Simple Reducers
- Reducers are a group of operations that reduces a stream of objects to a single object
- They are terminal operations
- They produce a result
- We cannot call any other operations after that
- e.g.
count()- It counts the number of elements in a stream
- It reduces a stream to the number of elements in that stream
anyMatch(predicate)allMatch(predicate)noneMatch(predicate)- See if any or all elements in a stream satisfy the condition
findFirst()findAny()max(comparator)min(comparator)- Find the maximum or minimum element based on a comparator
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("c", 15),
new Movie("b", 20),
);
movies.stream()
.count();
var result = movies.steram()
.anyMatch(m -> m.getLikes() > 20); // returns a boolean
movies.stream()
.findFirst()
.get();
movies.stream()
.max(Comparator.comparing(Movie::getLikes)) // maximum likes
.get();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Reducing a Stream
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("c", 15),
new Movie("b", 20),
);
// [10, 20, 30]
// [30, 30]
// [60]
// optional may have value or may not (when the stream is empty)
Optional<Integer> sum = movies.stream()
.map(Movie::getLikes)
.reduce((a, b) -> a + b);
System.out.println(sum.orElse(0)); // may have value or return the default 0 value
Integer sum = movies.stream()
.map(Movie::getLikes)
.reduce(0, Integer::sum);
System.out.println(sum); // may have value or return the default 0 value
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Collectors
- We want to collect the result of a stream into a data structure like a list, a set or a map
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10),
new Movie("c", 15),
new Movie("b", 20),
);
var result = movies.stream()
.filter(m -> m.getLikes() > 10)
.collect(Collectors.toList()); // return a stream into a list
.collect(Collectors.toSet());
.collect(Collectors.toMap(Movie::getTitle(), Movie::getLikes())); // key-value pair
.collect(Collectors.toMap(Movie::getTitle(), Function.identity())); // value is the movie object
.collect(Collectors.summingInt(Movie::getLikes)); // sum up the number of likes of all movies
.collect(Collectors.summingDouble(Movie::getLikes));
var result = movies.stream()
.filter(m -> m.getLikes() > 10)
.map(Movie::getTitle)
.collect(Collectors.joining(", ")) // return: b, c
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Grouping Elements
package com.codewithmosh.streams;
public enum Genre {
COMEDY,
ACTION,
THRILLER
}
public class Movie implements Comparable<Movie> {
private String title;
private int likes;
private Genre genre;
public Movie(String title, int likes, Genre genre) {
this.title = title;
this.likes = likes;
this.genre = genre;
}
public Genre getGenre() {
return genre;
}
}
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10, Genre.THRILLER),
new Movie("c", 15, Genre.ACTION),
new Movie("b", 20, Genre.ACTION),
);
var result = movies.stream()
.collect(Collectors.groupingBy(Movie::getGenre, Collectors.toSet()));
// or
.collect(Collectors.groupingBy(Movie::getGenre, Collectors.counting()));
// or
.collect(Collectors.groupingBy(
Movie::getGenre,
Collectors.mapping(
Movie::getTitle,
Collectors.joining())));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# Partitioning Elements
public class StreamsDemo {
public static void show() {
var movies = List.of(
new Movie("a", 10, Genre.THRILLER),
new Movie("c", 15, Genre.ACTION),
new Movie("b", 20, Genre.ACTION),
);
var result = movies.stream()
.collect(Collectors.partitioningBy(
m -> m.getLikes() > 20,
Collectors.mapping(
Movie::getTitle,
Collectors.joining(", "))));
// result = {false=a, b, true=c}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Primitive Type Streams

public class StreamsDemo {
public static void show() {
IntStream.rangeClosed(1,5)
.forEach(System.out::println); // print 1 to 4
}
}
1
2
3
4
5
6
2
3
4
5
6
上次更新: 2022/12/04, 16:55:22