Chapter 15 Functional Programming Flashcards

1
Q

Working with Built‐in Functional Interfaces

A
  • provided in the java.util.function package
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

IMPLEMENTING SUPPLIER

A

A Supplier is used when you want to generate or supply values without taking any input. The Supplier interface is defined as follows:

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
  • create a LocalDate object using the factory method now().
  • The LocalDate::now method reference is used to create a Supplier to assign to an intermediate variable s1.
Supplier<LocalDate> s1 = LocalDate::now;
Supplier<LocalDate> s2 = () -> LocalDate.now();
LocalDate d1 = s1.get();
LocalDate d2 = s2.get();
System.out.println(d1);
System.out.println(d2);
  • A Supplier is often used when constructing new objects.
  • we used a constructor reference to create the object.
  • using generics to declare what type of Supplier we are using.
Supplier<StringBuilder> s1 = StringBuilder::new;
Supplier<StringBuilder> s2 = () -> new StringBuilder();
System.out.println(s1.get());
System.out.println(s2.get());
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q
Supplier<ArrayList<String>> s3 = ArrayList<String>::new;
ArrayList<String> a1 = s3.get();
System.out.println(a1);

What would happen if we tried to print out s3 itself?

System.out.println(s3);
A

The code prints something like this:

functionalinterface.BuiltIns\$\$Lambda$1/0x0000000800066840@4909b8da
  • That’s the result of calling toString() on a lambda.
  • test class is named BuiltIns
  • in a package that we created named functionalinterface.
  • $$, which means that the class doesn’t exist in a class file on the file system. It exists only in memory.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

IMPLEMENTING CONSUMER AND BICONSUMER

A
  • 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.
  • The interfaces are defined as follows:
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
// omitted default method
}

@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
// omitted default method
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q
Consumer<String> c1 = System.out::println;
Consumer<String> c2 = x -> System.out.println(x);
c1.accept("Annie");
c2.accept("Annie");
A

This example prints Annie twice.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q
var map = new HashMap<String, Integer>();
BiConsumer<String, Integer> b1 = map::put;
BiConsumer<String, Integer> b2 = (k, v) -> map.put(k, v);
b1.accept("chicken", 7);
b2.accept("chick", 1);
System.out.println(map);
A
  • BiConsumer is called with two parameters.
  • They don’t have to be the same type.
  • The output is {chicken=7, chick=1},
  • which shows that both BiConsumer implementations did get called.
  • When declaring b1, we used an instance method reference on an object since we want to call a method on the local variable map.
  • The code to instantiate b1 is a good bit shorter than the code for b2.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q
var map = new HashMap<String, String>();
BiConsumer<String, String> b1 = map::put;
BiConsumer<String, String> b2 = (k, v) -> map.put(k, v);
b1.accept("chicken", "Cluck");
b2.accept("chick", "Tweep");
System.out.println(map);
A
  • The output is {chicken=Cluck, chick=Tweep},
  • which shows that a BiConsumer can use the same type for both the T and U generic parameters.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

IMPLEMENTING PREDICATE AND BIPREDICATE

A
  • You saw Predicate with removeIf() in Chapter 14.
  • Predicate is often used when filtering or matching.
  • A BiPredicate takes two parameters instead of one.
  • The interfaces are defined as follows:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
// omitted default and static methods
}

@FunctionalInterface
public interface BiPredicate<T, U> {
boolean test(T t, U u);
// omitted default methods
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q
Predicate<String> p1 = String::isEmpty;
Predicate<String> p2 = x -> x.isEmpty();
System.out.println(p1.test("")); // true
System.out.println(p2.test("")); // true
A

This prints true twice.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q
BiPredicate<String, String> b1 = String::startsWith;
BiPredicate<String, String> b2 = (string, prefix) -> string.startsWith(prefix);
System.out.println(b1.test("chicken", "chick")); // true
System.out.println(b2.test("chicken", "chick")); // true
A
  • The method reference includes both the instance variable and parameter for startsWith().
  • This is a good example of how method references save a good bit of typing.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

IMPLEMENTING FUNCTION AND BIFUNCTION

A
  • In Chapter 14, we used Function with the merge() method.
  • A Function is responsible for turning one parameter into a value of a potentially different type and returning it.
  • Similarly, a BiFunction is responsible for turning two parameters into a value and returning it.
  • The interfaces are defined as follows:
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
// omitted default and static methods
}

@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
// omitted default method
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q
Function<String, Integer> f1 = String::length;
Function<String, Integer> f2 = x -> x.length();
System.out.println(f1.apply("cluck")); // 5
System.out.println(f2.apply("cluck")); // 5
A
  • This function turns a String into an Int, which is autoboxed into an Integer
  • The types don’t have to be different.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q
BiFunction<String, String, String> b1 = String::concat;
BiFunction<String, String, String> b2 = (string, toAdd) -> string.concat(toAdd);
System.out.println(b1.apply("baby ", "chick")); // baby chick
System.out.println(b2.apply("baby ", "chick")); // baby chick
A
  • The first two types in the BiFunction are the input types.
  • The third is the result type.
  • For the method reference, the first parameter is the instance that concat() is called on, and the second is passed to concat().
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

CREATING YOUR OWN FUNCTIONAL INTERFACES

A
  • Java provides a built‐in interface for functions with one or two parameters.
  • You could create a functional interface such as this:

3 paramters:

@FunctionalInterface
interface TriFunction<T,U,V,R> {
R apply(T t, U u, V v);
}

4 parameters:

@FunctionalInterface
interface QuadFunction<T,U,V,W,R> {
R apply(T t, U u, V v, W w);
}

Remember that you can add any functional interfaces you’d like, and Java matches them when you use lambdas or method references.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

IMPLEMENTING UNARYOPERATOR AND BINARYOPERATOR

A
  • UnaryOperator and BinaryOperator are a special case of a Function.
  • They require all type parameters to be the same type.
  • A UnaryOperator transforms its value into one of the same type.
  • UnaryOperator extends Function.
  • A BinaryOperator merges two values into one of the same type.
  • BinaryOperator extends BiFunction.
  • The interfaces are defined as follows:
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> { }

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T, T, T> {
// omitted static methods
}

method signatures look like this:

T apply(T t); // UnaryOperator

T apply(T t1, T t2); // BinaryOperator
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q
UnaryOperator<String> u1 = String::toUpperCase;
UnaryOperator<String> u2 = x -> x.toUpperCase();
System.out.println(u1.apply("chirp")); // CHIRP
System.out.println(u2.apply("chirp")); // CHIRP
A

This prints CHIRP twice.

We don’t need to specify the return type in the generics because UnaryOperator requires it to be the same as the parameter

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q
BinaryOperator<String> b1 = String::concat;
BinaryOperator<String> b2 = (string, toAdd) -> string.concat(toAdd);
System.out.println(b1.apply("baby ", "chick")); // baby chick
System.out.println(b2.apply("baby ", "chick")); // baby chick
A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

CHECKING FUNCTIONAL INTERFACES

What functional interface would you use in these three situations?

  1. Returns a String without taking any parameters
  2. Returns a Boolean and takes a String
  3. Returns an Integer and takes two Integers
A
  1. Supplier<String>
  2. Function<String, Boolean>
    Predicate<String>. Note that a Predicate returns a boolean primitive and not a Boolean object.
  3. BinaryOperator<Integer> or a BiFunction<Integer,Integer,Integer>
    BinaryOperator<Integer> is the better answer of the two since it is more specific.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

What functional interface would you use to fill in the blank for these?

6: \_\_\_\_\_\_\_<List> ex1 = x -> "".equals(x.get(0));
7: \_\_\_\_\_\_\_<Long> ex2 = (Long l) -> System.out.println(l);
8: \_\_\_\_\_\_\_<String, String> ex3 = (s1, s2) -> false;
A
  • Line 6 Predicate
    Since the generic declaration has only one parameter, it is a Predicate.
  • Line 7 passes one Long parameter to the lambda and doesn’t return anything. This tells us that it is a Consumer.
  • Line 8, there are two parameters, so it is a BiPredicate.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

These are meant to be tricky:

6: Function<List<String>> ex1 = x -> x.get(0); // DOES NOT COMPILE
7: UnaryOperator<Long> ex2 = (Long l) -> 3.14; // DOES NOT COMIPLE
8: Predicate ex4 = String::isEmpty; // DOES NOT COMPILE
A
  • Line 6 claims to be a Function. A Function needs to specify two generics—the input parameter type and the return value type. The return value type is missing from line 6, causing the code not to compile.
  • Line 7 is a UnaryOperator, which returns the same type as it is passed in. The example returns a double rather than a Long, causing the code not to compile.
  • Line 8 is missing the generic for Predicate. This makes the parameter that was passed an Object rather than a String. The lambda expects a String because it calls a method that exists on String rather than Object. Therefore, it doesn’t compile.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

CONVENIENCE METHODS ON FUNCTIONAL INTERFACES

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

It’s a bit long to read, and it contains duplication.
What if we decide the letter e should be capitalized in egg?

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

Predicate<String> brownEggs = s -> s.contains("egg") && s.contains("brown");
Predicate<String> otherEggs = s -> s.contains("egg") && ! s.contains("brown");
A
  • reusing the logic in the original Predicate variables to build two new ones.
  • It’s shorter and clearer what the relationship is between variables.
  • We can also change the spelling of egg in one place, and the other two objects will have new logic because they reference it.
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());
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q
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
A
  • andThen() method, which runs two functional interfaces in sequence.
  • Notice how the same parameter gets passed to both c1 and c2.
  • This shows that the Consumer instances are run in sequence and are independent of each other.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q
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
A
  • compose() method on Function chains functional interfaces.
  • This time the before runs first, turning the 3 into a 4.
  • Then the after runs, doubling the 4 to 8.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

Returning an Optional

A
  • An Optional is created using a factory.
  • You can either request an empty Optional or pass a value for the Optional to wrap.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
26
Q

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: }
A
  • Line 11 returns an empty Optional when we can’t calculate an average.
    Optional.empty()
  • Lines 12 and 13 add up the scores.
  • There is a functional programming way of doing this math, but we will get to that later in the chapter. In fact, the entire method could be written in one line, but that wouldn’t teach you how Optional works!
  • Line 14 creates an Optional to wrap the average.
    Optional.of((double) sum / scores.length)

When creating an Optional, 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);

Java provides a factory method to do the same thing.

Optional o = Optional.ofNullable(value);

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
27
Q
20: Optional<Double> opt = average(90, 100);
21: if (opt.isPresent())
22: System.out.println(opt.get()); // 95.0
A
  • Line 21 checks whether the Optional actually contains a value.
  • Line 22 prints it out.

if we didn’t do the check and the Optional was empty

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

We’d get an exception since there is no value inside the Optional.

java.util.NoSuchElementException: No value present
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
28
Q

DEALING WITH AN EMPTY OPTIONAL

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
29
Q
30: Optional<Double> opt = average();
31: System.out.println(opt.orElse(Double.NaN));
32: System.out.println(opt.orElseGet(() -> Math.random()));
A

This prints something like the following:

NaN
0.49775932295380165
  • Line 31 shows that you can return a specific value or variable. In our case, we print the “not a number” value.
  • Line 32 shows using a Supplier to generate a value at runtime to return instead. I’m glad our professors didn’t give us a random average, though!
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
30
Q
30: Optional<Double> opt = average();
31: System.out.println(opt.orElseThrow());
A

This prints something like the following:

Exception in thread "main" java.util.NoSuchElementException:
No value present
at java.base/java.util.Optional.orElseThrow(Optional.java:382)
  • Without specifying a Supplier for the exception, Java will throw a NoSuchElementException.
  • This method was added in Java 10. Remember that the stack trace looks weird because the lambdas are generated rather than named classes.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
31
Q
30: Optional<Double> opt = average();
31: System.out.println(opt.orElseThrow(
32: () -> new IllegalStateException()));
A

This prints something like the following:

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)
  • Line 32 shows using a Supplier to create an exception that should be thrown. Notice that we do not write throw new IllegalStateException().
  • The orElseThrow() method takes care of actually throwing the exception when we run it.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
32
Q
System.out.println(opt.orElseGet(
() -> new IllegalStateException())); // DOES NOT COMPILE
A
  • The opt variable is an Optional<Double>.
  • This means the Supplier must return a Double.
  • Since this supplier returns an exception, the type does not match.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
33
Q
Optional<Double> opt = average(90, 100);
System.out.println(opt.orElse(Double.NaN));
System.out.println(opt.orElseGet(() -> Math.random()));
System.out.println(opt.orElseThrow());
A

It prints out 95.0 three times. Since the value does exist, there is no need to
use the “or else” logic.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
34
Q

IS OPTIONAL THE SAME AS NULL?

A

Optional is a clear statement in the API that there might not be a value in there.

35
Q

Using Streams

A

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.

36
Q

UNDERSTANDING THE PIPELINE FLOW

A

Finite streams

infinite streams

With streams, the data isn’t generated up front—it is created when needed.
This is an example of lazy evaluation, which delays execution until necessary.

stream operations

There are three parts to a stream pipeline

  • Source: Where the stream comes from
  • Intermediate operations: Transforms the stream into another one. There can be as few or as many intermediate operations as you’d like. Since streams use lazy evaluation, the intermediate operations do not run until the terminal operation runs.
  • Terminal operation: Actually produces a result. Since streams can be used only once, the stream is no longer valid after a terminal operation completes.

stream pipeline concept

  • A factory typically has a foreman who oversees the work.
  • Java serves as the foreman when working with stream pipelines.
  • This is a really important role, especially when dealing with lazy evaluation and infinite streams.
  • Think of declaring the stream as giving instructions to the foreman.
  • As the foreman finds out what needs to be done, he sets up the stations and tells the workers what their duties will be.
  • The foreman waits until he sees the terminal operation to actually kick off the work.
  • He also watches the work and stops the line as soon as work is complete.
37
Q

CREATING STREAM SOURCES

A

Stream<T> interface
defined in the java.util.stream package.</T>

38
Q

Creating Finite Streams

A

There are a few ways to create them.

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
  • Line 11 shows how to create an empty stream.
  • Line 12 shows how to create a stream with a single element.
  • Line 13 shows how to create a stream from a varargs. You’ve undoubtedly noticed that there isn’t an array on line 13.
  • The method signature uses varargs, which let you specify an array or individual
    elements.

Java also provides a convenient way of converting a Collection to a stream.

14: var list = List.of("a", "b", "c");
15: Stream<String> fromList = list.stream();

Line 15 shows that it is a simple method call to create a stream from a list.

39
Q

CREATING A PARALLEL STREAM

A
24: var list = List.of("a", "b", "c");
25: Stream<String> fromListParallel = list.parallelStream();

some tasks cannot be done in parallel

aware that there is a cost in coordinating the work, so for smaller streams, it might be faster to do it sequentially.

40
Q

Creating Infinite Streams

A
17: Stream<Double> randoms = Stream.generate(Math::random);
18: Stream<Integer> oddNumbers = Stream.iterate(1, n -> n + 2);
  • Line 17 generates a stream of random numbers. How many random numbers? However many you need. If you call randoms.forEach(System.out::println), the program will print random numbers until you kill it. Later in the chapter, you’ll learn about operations like limit() to turn the infinite stream into a finite stream.
  • Line 18 gives you more control. The iterate() method takes a seed or starting value as the first parameter. This is the first element that will be part of the stream. The other parameter is a lambda expression that gets passed the previous value and generates the next value. As with the random numbers example, it will keep on producing odd numbers as long as you need them.
41
Q

What if you wanted just odd numbers less than 100?

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
A

Java 9 introduced an overloaded version of iterate()

This method takes three parameters.
Notice how they are separated by commas ( ,) just like all other methods.

42
Q

Reviewing Stream Creation Methods

A
  1. Stream.empty()
  2. Stream.of(varargs)
  3. coll.stream()
  4. coll.parallelStream()
  5. Stream.generate(supplier)
  6. Stream.iterate(seed, unaryOperator)
  7. Stream.iterate(seed, predicate, unaryOperator)
43
Q

USING COMMON TERMINAL OPERATIONS

A
  • 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 of the contents of the stream are combined into a single primitive or Object.
  1. count()
  2. min()
    max()
  3. findAny()
    findFirst()
  4. allMatch()
    anyMatch()
    noneMatch()
  5. forEach()
  6. reduce()
  7. collect()
44
Q

count()s

A

The count() method determines the number of elements in a finite stream.
For an infinite stream, it never terminates.

The count() method is a reduction because it looks at each element in the stream and returns a single value. The method signature is as follows:

long count()

ex:

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

min() and max()

A

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.

min() and max() hang on an infinite stream

Both methods are reductions because they return a single value after looking at the entire stream. The method signatures are as follows:

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

Notice that the code returns an Optional rather than the value.
ex:

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

ex:

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

What if you need both the min() and max() values of the same stream?

A

you can’t have both
stream can have only one terminal operation
there are built‐in summary methods for some numeric streams that will calculate a set of values for you.

47
Q

findAny() and findFirst()

A

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.

the findAny() method can return any element of the stream.
it commonly returns the first element, although this behavior is not guaranteed.
the findAny() method is more likely to return a random element when working with parallel streams.

These methods are terminal operations but not reductions.

The reason is that they sometimes return without processing all of the elements. This means that they return a value based on the stream but do not reduce the entire stream into one value.

The method signatures are as follows:

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

ex:

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
48
Q

allMatch() anyMatch() and noneMatch()

A
  • 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. It depends on the data.
  • Like the find methods, they are not reductions because they do not necessarily look at all of the elements.

The method signatures are as follows:

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

ex:

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

On the infinite stream, one match is found, so the call terminates.
If we called allMatch(), it would run until we killed the program.

49
Q
  • Remember that allMatch(), anyMatch(), and noneMatch() return a boolean.
  • By contrast, the find methods return an Optional because they return an element of the stream.
A
50
Q

forEach()

A

calling forEach() on an infinite stream does not terminate.
Since there is no return value, it is not a reduction.
Before you use it, consider if another approach would be better.

The method signature is as follows:

void forEach(Consumer<? super T> action)

only terminal operation with a return type of void.
If you want something to happen, you have to make it happen in the Consumer.

ex:

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

reduce()

A
  • The reduce() method combines a stream into a single object.
  • It is a reduction, which means it processes all elements.
  • The three method signatures are these:
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
he accumulator combines the current result with the current value in the stream.

ex:

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

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
52
Q

In many cases, the identity isn’t really necessary, so Java lets us omit it. When you don’t specify an identity, an Optional is returned because there might not be any data.

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.
A
53
Q
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
A
54
Q

collect()

A

test

55
Q

USING COMMON INTERMEDIATE OPERATIONS

A

test

56
Q

filter()

A

test

57
Q

distinct()

A

test

58
Q

limit() and skip()

A

test

59
Q

map()

A

test

60
Q

flatMap()

A

test

61
Q

sorted()

A

test

62
Q

peek()

A

test

63
Q

DANGER: CHANGING STATE WITH PEEK()

A

test

64
Q

PUTTING TOGETHER THE PIPELINE

A

test

65
Q

PEEKING BEHIND THE SCENES

A

test

66
Q

Working with Primitive Streams

A

test

67
Q

CREATING PRIMITIVE STREAMS

A

test

68
Q

MAPPING STREAMS

A

test

69
Q

USING FLATMAP()

A

test

70
Q

USING OPTIONAL WITH PRIMITIVE STREAMS

A

test

71
Q

SUMMARIZING STATISTICS

A

test

72
Q

LEARNING THE FUNCTIONAL INTERFACES FOR PRIMITIVES

A

test

73
Q

Functional Interfaces for boolean

A

test

74
Q

Functional Interfaces for double int and long

A

test

75
Q

Working with Advanced Stream Pipeline Concepts

A

test

76
Q

LINKING STREAMS TO THE UNDERLYING DATA

A

test

77
Q

CHAINING OPTIONALS

A

test

78
Q

CHECKED EXCEPTIONS AND FUNCTIONAL INTERFACES

A

test

79
Q

COLLECTING RESULTS

A

test

80
Q

Collecting Using Basic Collectors

A

test

81
Q

Collecting into Maps

A

test

82
Q

Collecting Using Grouping Partitioning and Mapping

A

test

83
Q

DEBUGGING COMPLICATED GENERICS

A

test