CONTENTS
1. Java8 support for streams
A stream is a sequence of elements that is not tied to any particular storage mechanism. Unlike traversing elements in a collection, when using streams, we extract elements from a pipeline and perform operations on them. These pipes are usually connected in series to form an operation pipeline on the stream.
A core advantage of streams is that they make our programs smaller and easier to understand. Lambda expressions and method references come into their own when used with streams. Streams greatly increase the appeal of Java 8.
Suppose we want to display randomly selected non-repeating integers in the range of 5 to 20 in an orderly manner. With the help of streams, we only need to explain what we want to do:
package stream;
import java.util.Random;
public class Randoms {
public static void main(String[] args) {
new Random(47)
.ints(5, 20)
.distinct().limit(5)
.sorted()
.forEach(System.out::println);
/*
* 6
* 10
* 13
* 16
* 18
*/
}
}
We first set a random seed for theRandom
object. The ints()
method will generate a stream. This method has multiple overloaded versions, two of which Versions of the parameter can set upper and lower bounds on the generated values. Use intermediate flow operation distinct()
to remove duplicate values, and then use limit()
to select the first 5 values, sorted()
means the elements are ordered , and finally we want to display each entry, so we use forEach()
, which will perform an operation on each stream object based on the function we pass. Here we pass a method reference System.out::println
to display each entry on the console.
We do not see any explicit iteration mechanism in the code implemented using streams, so it is calledinternal iteration, which is stream programming. A core feature.
2. Creation of streams
You can easily turn a set of items into a stream using Stream.of()
:
package stream;
import java.util.stream.Stream;
public class StreamOf {
public static void main(String[] args) {
Stream.of(4, 1, 7).forEach(System.out::println);
Stream.of("Hello ", "World ", "AsanoSaki").forEach(System.out::print);
/*
* 4
* 1
* 7
* Hello World AsanoSaki
*/
}
}
In addition, each Collection
can use the stream()
method to generate a stream:
package stream;
import java.util.*;
import java.util.stream.Collectors;
class A {
int x;
A(int x) {
this.x = x; }
}
public class CollectionToStream {
public static void main(String[] args) {
List<A> listA = Arrays.asList(new A(1), new A(2), new A(3));
System.out.println(listA.stream().mapToInt(a -> a.x).sum());
Set<String> st = new TreeSet<>(Arrays.asList("Hello world and java".split(" ")));
System.out.println(st.stream().map(String::toUpperCase).collect(Collectors.joining(" ")));
Map<String, Double> mp = new HashMap<>();
mp.put("PI", 3.14);
mp.put("E", 2.718);
mp.put("PHI", 1.618);
mp.entrySet().stream()
.map(e -> e.getKey() + ": " + e.getValue())
.forEach(System.out::println);
/*
* 6
* HELLO AND JAVA WORLD
* PHI: 1.618
* E: 2.718
* PI: 3.14
*/
}
}
After creating a List<A>
, you only need to call stream()
, a method that all collection classes have. The middle map()
operation accepts each element in the stream, applies an operation on it to create a new element, and then passes the new element along the stream. Here . and . There are similarly named operations for containing mapToInt()
Converts an object stream into an Integer
IntStream
Float
Double
collect()
The operation combines all the stream elements according to its parameters. When we use Collectors.joining()
, the result is a String
for each element. They will all be separated by joining()
parameters, and there are many others Collectors
that can generate different results.
To generate a stream from the Map
collection, we first call entrySet()
to generate a stream of objects, where each object contains a key and its associated values, and then separate them using getKey()
and getValue()
.
2.1 Random number stream
Ramdom
Classes introduced streams in Java 8, with a set of methods that can generate streams:
package stream;
import java.util.Random;
import java.util.stream.Stream;
public class RandomGenerators {
public static <T> void show(Stream<T> stream) {
stream.limit(3).map(x -> x + " ").forEach(System.out::print);
System.out.println();
}
public static void main(String[] args) {
Random rand = new Random(47);
show(rand.ints().boxed());
show(rand.doubles().boxed());
show(rand.ints(10, 20).boxed()); // 设置上下边界
show(rand.ints(2).boxed()); // 设置流的大小
show(rand.ints(2, 10, 20).boxed()); // 设置流的大小与上下边界
/*
* -1172028779 1717241110 -2014573909
* 0.053412216308810656 0.5779976127815049 0.4170137422770571
* 17 18 18
* 1122537102 491149179
* 19 18
*/
}
}
To eliminate redundant code, the above example createsgenericmethodshow(Stream<T> stream)
. This feature is Will talk about it later. The type parameter T
can be anything, so using Integer
, Long
and Double
will work. However, the Random
class will only generate values of the basic types int
, long
, and double
. Fortunately, boxed()
stream operations automatically convert the base type to its corresponding wrapper type, allowing show()
to accept the stream.
2.2 Interval range of integer type
IntStream
The class provides a range()
method that can generate a stream (a sequence composed of int
values), which is very convenient when writing loops: a>
package stream;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class IntStreamRange {
public static void main(String[] args) {
// for-in循环
for (int i: IntStream.range(1, 5).toArray())
System.out.print(i + " "); // 1 2 3 4
System.out.println();
// Stream
System.out.println(IntStream.range(1, 5).boxed().map(Object::toString).collect(Collectors.joining(" "))); // 1 2 3 4
IntStream.range(1, 5).boxed().forEach(x -> System.out.print(x + " ")); // 1 2 3 4
System.out.println();
}
}
Now we write a repeat()
utility function to replace the simple for
loop:
package stream;
import java.util.stream.IntStream;
public class Repeat {
static void repeat(int n, Runnable action) {
IntStream.range(0, n).forEach(i -> action.run());
}
static void hello() {
System.out.println("Hello");
}
public static void main(String[] args) {
repeat(3, () -> System.out.println("Lambda"));
repeat(2, Repeat::hello);
/*
* Lambda
* Lambda
* Lambda
* Hello
* Hello
*/
}
}
2.3 Stream.generate()
Stream.generate()
can accept any Supplier<T>
(interface in java.util.function
) and generate an object composed of T
type of flow. If you want to create a stream consisting of the exact same objects, just pass a Lambda expression that generates these objects to generate()
:
package stream;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Generator implements Supplier<String> {
Random rand = new Random(47);
char[] letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
@Override
public String get() {
return String.valueOf(letters[rand.nextInt(letters.length)]);
}
public static void main(String[] args) {
String str = Stream.generate(new Generator()).limit(10).collect(Collectors.joining());
System.out.println(str); // YNZBRNYGCF
Stream.generate(() -> "AsanoSaki").limit(2).forEach(s -> System.out.print(s + " ")); // AsanoSaki AsanoSaki
}
}
2.4 Stream.iterate()
Stream.iterate()
Starts with a seed (the first parameter) and passes it to the method referenced by the second parameter. The result is added to the stream and saved for next time< a i=1> The first parameter of the call, and so on. We can generate a previously implemented Fibonacci sequence by iteration:iterate()
package stream;
import java.util.stream.Stream;
public class StreamFibonacci {
int x = 1;
Stream<Integer> fib() {
return Stream.iterate(0, i -> {
int res = x + i;
x = i;
return res;
});
}
public static void main(String[] args) {
new StreamFibonacci().fib().limit(10).forEach(x -> System.out.print(x + " "));
System.out.println();
new StreamFibonacci().fib().skip(10).limit(10).forEach(x -> System.out.print(x + " "));
/*
* 0 1 1 2 3 5 8 13 21 34
* 55 89 144 233 377 610 987 1597 2584 4181
*/
}
}
iterate()
will only remember the result, so you must use x
to remember another element. We used the skip()
operation, which has not been introduced before. It will directly discard the corresponding number of stream elements specified by its parameters. The first 10 are discarded here.
2.5 Stream generator
InBuilder (Builder) design pattern, we create a generator object, provide it with multiple pieces of construction information, and finally execute< /span>, assuming we implement reading each line of the file and converting it into a word stream: library provides such a (build) action. The GenerateStream
Builder
package stream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class FileToWordsBuilder {
Stream.Builder<String> builder = Stream.builder();
public FileToWordsBuilder(String filePath) throws Exception {
Files.lines(Paths.get(filePath)).forEach(line -> {
for (String s: line.split("[ .?,!]+"))
builder.add(s);
});
}
public static void main(String[] args) throws Exception {
String filePath = "src/file/FileToWordsBuilder.txt";
new FileToWordsBuilder(filePath).builder.build()
.forEach(s -> System.out.print(s + " "));
/*
* Hello world Today is Saturday Have a nice day
*/
}
}
The text content is as follows:
Hello world!
Today is Saturday?
Have a nice day!
The constructor adds all the words in the file, but it does not call build()
, which means that as long as build()
is not called, you can continue to a>Builder
Add words to the object. If you try to add a word to after calling build()
, an exception will be thrown. Stream.Builder
2.6 Arrays
Arrays
The class contains a static method named stream()
that converts an array into a stream. Can be any object array, or the int
, long
and double
primitive types, yielding IntStream
, LongStream
and DoubleStream
:
package stream;
import java.util.Arrays;
public class ArraysStream {
public static void main(String[] args) {
Arrays.stream(new String[] {
"ABC", "XYZ", "YYJ" })
.forEach(s -> System.out.print(s + " "));
System.out.println();
Arrays.stream(new int[] {
1, 2, 3 })
.forEach(x -> System.out.printf("%d ", x));
System.out.println();
Arrays.stream(new double[] {
3.14159, 2.718, 1.618 })
.forEach(x -> System.out.printf("%.2f ", x));
System.out.println();
Arrays.stream(new int[] {
9, 8, 7, 6, 5 }, 1, 4)
.forEach(x -> System.out.printf("%d ", x));
System.out.println();
/*
* ABC XYZ YYJ
* 1 2 3
* 3.14 2.72 1.62
* 8 7 6
*/
}
}
The last callstream()
used two additional parameters. The first one indicates where in the array to start selecting elements, and the second one indicates the stopping position (open interval), That is, in this example, select the element [1, 4)
in the array.
3. Intermediate operations
3.1 Sorting stream elements
Usesorted()
for default sorting (from small to large). This method can also accept a Comparator
parameter, which can be passed in a Lambda expression. You can also use the predefined Comparator
:
package stream;
import java.util.Comparator;
import java.util.stream.Stream;
public class StreamSorted {
public static void main(String[] args) {
Stream.of(1, 2, 3)
.sorted(Comparator.reverseOrder())
.forEach(x -> System.out.print(x + " "));
/*
* 3 2 1
*/
}
}
3.2 Remove elements
distinct()
: Remove duplicate elements from the stream.filter(Predicate)
: Filter only retains elements that meet specific conditions, that is, stream elements that satisfy the filter functionPredicate
totrue
.
Let’s look at an example of filtering prime numbers:
package stream;
import java.util.stream.LongStream;
public class Prime {
static boolean isPrime(long n) {
return LongStream.rangeClosed(2, (long)Math.sqrt(n)).noneMatch(i -> n % i == 0);
}
static LongStream getPrime() {
return LongStream.iterate(2, i -> i + 1).filter(Prime::isPrime);
}
public static void main(String[] args) {
Prime.getPrime().limit(10).forEach(x -> System.out.printf("%d ", x));
System.out.println();
/*
* 2 3 5 7 11 13 17 19 23 29
*/
}
}
rangeClosed()
contains the upper bound value. If the result of any remainder operation on the elements in the stream is 0, then the noneMatch()
operation returns true
, if If any calculation result is equal to 0, returnfalse
. noneMatch()
will exit after the first failure without trying all subsequent calculations.
4. Optional type
Before studying the finalization operation, we must consider a question: What will happen if we request an object from the stream, but there is nothing in the stream? Is there some kind of object we can use that can both serve as a placeholder for a stream element and also notify us nicely (that is, without throwing an exception) when the element we're looking for doesn't exist?
This idea is implemented as Optional
type. Some standard stream operations will return Optional
objects because they cannot guarantee that the desired result will exist. These stream operations are listed below:
findFirst()
: ReturnsOptional
containing the first element. If this stream is empty, returnsOptional.empty
.findAny()
: ReturnsOptional
containing any element. If this stream is empty, returnsOptional.empty
.max()
andmin()
returnOptional
containing the maximum or minimum value in the stream respectively. If the stream is empty, returnOptional.empty
.reduce()
A version of that does not take aidentity
object as its first argument (in other versions ofreduce()
,identity
object will become the default result, so there is no risk of a null result), which will wrap the return value in aOptional
.- For numeric streams
IntStream
,LongStream
, andDoubleStream
, theaverage()
operation Wrap the result in aOptional
in case the stream is empty.
Take a look at the following code sample:
package stream;
import java.util.Optional;
import java.util.stream.Stream;
public class StreamOptional {
static void test(Optional<String> stringOptional) {
if (stringOptional.isPresent())
System.out.println(stringOptional.get());
else
System.out.println("Nothing in Optional!");
}
public static void main(String[] args) {
System.out.println(Stream.<String>empty().findFirst());
test(Stream.of("Hello").findFirst());
test(Stream.<String>empty().findFirst());
/*
* Optional.empty
* Hello
* Nothing in Optional!
*/
}
}
At this time, an exception will not be thrown because the stream is empty, but a Optional.empty
object will be obtained. Optional
There is a toString()
method that displays useful information.
Note that the empty stream is created by Stream.<String>empty()
. If only Stream.empty()
is used without any context information, then Java does not know what it should be. type, and this syntax solves that problem. If the compiler has enough contextual information, it can infer the type of the empty()
call, like this:
Stream<String> s = Stream.empty();
When we receive a Optional
, we must first call isPresent()
to see if there is anything in it. If so, use it again get()
to get.
4.1 Convenience functions
There are a number of convenience functions that can be used to obtain the data in Optional
, which simplify the above process of first checking and then processing the contained object:
ifPresent(Consumer)
: If the object exists, use this object to callConsumer
, otherwise do nothing.orElse(otherObject)
: If the object exists, return this object, otherwise returnotherObject
.orElseGet(Supplier)
: If the object exists, return this object, otherwise return the replacement object created using theSupplier
function .orElseThrow(Supplier)
: If the object exists, return this object, otherwise throw an exception created using theSupplier
function.
package stream;
import java.util.Optional;
import java.util.stream.Stream;
public class OptionalFunctions {
static Optional<String> emptyOptional = Stream.<String>empty().findFirst();
static Optional<String> stringOptional = Stream.of("Hello").findFirst();
public static void main(String[] args) {
System.out.println("---------- ifPresent ----------");
emptyOptional.ifPresent(System.out::println);
stringOptional.ifPresent(System.out::println);
System.out.println("---------- orElse ----------");
System.out.println(emptyOptional.orElse("Other String"));
System.out.println(stringOptional.orElse("Other String"));
System.out.println("---------- orElseGet ----------");
System.out.println(emptyOptional.orElseGet(() -> "Other Supplier Object"));
System.out.println(stringOptional.orElseGet(() -> "Other Supplier Object"));
System.out.println("---------- orElseThrow ----------");
try {
System.out.println(stringOptional.orElseThrow(() -> new Exception("Supplier Exception"))); // 先执行非空Optional
System.out.println(emptyOptional.orElseThrow(() -> new Exception("Supplier Exception")));
} catch (Exception e) {
System.out.println("Caught" + e);
}
/*
* ---------- ifPresent ----------
* Hello
* ---------- orElse ----------
* Other String
* Hello
* ---------- orElseGet ----------
* Other Supplier Object
* Hello
* ---------- orElseThrow ----------
* Hello
* Caughtjava.lang.Exception: Supplier Exception
*/
}
}
4.2 Create Optional
When you need to write the code to generate Optional
yourself, there are three static methods that can be used:
empty()
: Returns an emptyOptional
.of(value)
: If you already know that thisvalue
is notnull
, you can use this method to wrap it in aOptional
.ofNullable(value)
: If you don’t know whether thisvalue
isnull
, you can use this method, ifvalue
is . in a , otherwise it will wrap thisnull
, it will automatically returnOptional.empty
value
Optional
Let’s take a look at an example:
package stream;
import java.util.Optional;
public class CreatingOptionals {
static Optional<String>
stringOptional = Optional.of("Hello"),
emptyOptional = Optional.empty(),
nullOptional = Optional.ofNullable(null);
public static void main(String[] args) {
System.out.println(stringOptional.orElse("Empty"));
System.out.println(emptyOptional.orElse("Empty"));
System.out.println(nullOptional.orElse("Empty"));
}
/*
* Hello
* Empty
* Empty
*/
}
4.3 Operations on Optional objects
There are three ways to post-process Optional
, so if your streaming pipeline generates a Optional
, you can do one more at the end Processing:
filter(Predicate)
: AppliesPredicate
to the contents ofOptional
and returns its result. IfOptional
does not matchPredicate
, convert it toempty
. IfOptional
itself is alreadyempty
, it will be returned directly.map(Function)
: IfOptional
is notempty
, applyFunction
toOptional
The object contained in and returns the result, otherwiseOptional.empty
is returned.flatMap(Function)
: Similar tomap()
, but the provided mapping function wraps the result inOptional
so thatflatMap()
In the end, there won’t be any packaging.
These operations are not provided on numeric Optional
.
Let’s take a look at the usage of filter
:
package stream;
import java.util.Arrays;
public class OptionalFilter {
static String[] elements = {
"Dog", "Cat", "", "Bird" };
public static void main(String[] args) {
for (int i = 0; i <= elements.length; i++)
System.out.println(
Arrays.stream(elements)
.skip(i)
.findFirst()
.filter(s -> s.length() == 3)
);
/*
* Optional[Dog]
* Optional[Cat]
* Optional.empty
* Optional.empty
* Optional.empty
*/
}
}
Although the output looks like a stream, each time it enters the for
loop, it will reacquire a stream and skip the loop with for
The index sets the number of elements, which makes it look like consecutive elements in the stream, and then it does findFirst()
, getting the first of the remaining elements, which will be wrapped in a < Returned in /span>Optional
.
Note that our for
loop loops to i == elements.length
, so the last element will exceed the stream. But this will automatically change to Optional.empty
.
4.4 Stream composed of Optional
Suppose there is a generator that may generate null
values. If a stream is created using this generator, we naturally want to wrap these elements in Optional
, when using this stream, we have to figure out how to get the object in Optional
:
package stream;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Stream;
class Signal {
private final String msg;
static Random rand = new Random(47);
Signal(String msg) {
this.msg = msg; }
@Override
public String toString() {
return "Signal(" + msg + ")";
}
public static Signal getSignal() {
switch (rand.nextInt(4)) {
case 1: return new Signal("Case 1");
case 2: return new Signal("Case 2");
default: return null;
}
}
public static Stream<Optional<Signal>> getSignalOptStream() {
return Stream.generate(Signal::getSignal).map(Optional::ofNullable);
}
}
public class StreamOfOptionals {
public static void main(String[] args) {
Signal.getSignalOptStream().limit(5).forEach(System.out::println);
System.out.println("--------------------");
Signal.getSignalOptStream().limit(5)
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println);
/*
* Optional[Signal(Case 2)]
* Optional[Signal(Case 1)]
* Optional[Signal(Case 2)]
* Optional.empty
* Optional.empty
* --------------------
* Signal(Case 2)
* Signal(Case 1)
* Signal(Case 2)
* Signal(Case 2)
*/
}
}
Here I use only that is not empty
, and then call =4> to get the object wrapped in it, because each case requires us to decide what "no value" means, so we usually need to take a different approach for each application. Optional
map()
get()
5. Termination operation
These operations take a stream and produce a final result; they do not send anything to a backend stream. Therefore, finalization is always the last thing we can do in a pipeline.
5.1 Convert a stream to an array
toArray()
: Convert stream elements into an array of appropriate type.toArray(generator)
:generator
is used to allocate its own array storage in specific situations.
Let’s look at the example directly:
package stream;
import java.util.Arrays;
import java.util.Random;
public class RandInts {
public static void main(String[] args) {
int[] nums = new Random(47).ints(0, 10).limit(10).toArray();
System.out.println(Arrays.toString(nums)); // [8, 5, 3, 1, 1, 9, 8, 0, 2, 7]
}
}
5.2 Apply a finalization operation to each stream element
forEach(Consumer)
: We have seen this usage many times, usingSystem.out::println
as aConsumer
function.forEachOrdered(Consumer)
: This version ensures thatforEach
operates on elements in the order of the original stream.
The first form is explicitly designed to operate on elements in any order, which only makes sense when the parallel()
operation is introduced. parallel()
Let Java try to perform the operation on multiple processors. It can do this precisely because it uses streams, which it can split into multiple streams (usually one stream per processor) and run each stream on a different processor.
We introduce parallel()
in the following example to understand the role and necessity of forEachOrdered()
:
package stream;
import java.util.Arrays;
import java.util.Random;
public class ForEachOrdered {
public static void main(String[] args) {
int[] nums = new Random(47).ints(0, 10).limit(10).toArray(); // 确保每次流元素都相同
Arrays.stream(nums).forEach(x -> System.out.printf("%d ", x));
System.out.println();
Arrays.stream(nums).parallel().forEach(x -> System.out.printf("%d ", x));
System.out.println();
Arrays.stream(nums).parallel().forEachOrdered(x -> System.out.printf("%d ", x));
System.out.println();
/*
* 8 5 3 1 1 9 8 0 2 7
* 8 9 0 7 2 3 5 1 8 1
* 8 5 3 1 1 9 8 0 2 7
*/
}
}
In the first stream, we did not use parallel()
, so the results are displayed in the order they appear from Arrays.stream(nums)
. The second stream introduces parallel()
. Even for such a small stream, we can see that the order of output is different from before. This is because there are multiple processors processing this problem, and if you run this program multiple times, you will find that the output will be different each time. This is due to the uncertainty caused by multiple processors processing this problem at the same time. factor.
The last stream still uses parallel()
, but uses forEachOrdered()
to force the results back to the original order. Therefore, using for streams other than parallel()
will have no effect. forEachOrdered()
5.3 Collection operations
collect(Collector)
: Use thisCollector
to accumulate stream elements into a result set.collect(Supplier, BiConsumer, BiConsumer)
: Similar to above, butSupplier
will create a new result set, the firstBiConsumer
is used to include the next element into the result function in , the second oneBiConsumer
is used to combine two values.
We have seen only a few examples of Collectors
objects before, and we can collect stream elements into any particular kind of collection. Suppose we want to end up with our items in a TreeSet
so that they are always ordered. There is no specific method in Collectors
, only , but it is possible to use , and Pass it a constructor reference of any type. The following program extracts words from the file and puts them into :toTreeSet()
toSet()
Collectors.toCollection()
Collection
TreeSet
package stream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
public class TreeSetOfWords {
public static void main(String[] args) throws Exception {
String filePath = "src/file/FileToWordsBuilder.txt";
Set<String> words = Files.lines(Paths.get(filePath))
.flatMap(line -> Arrays.stream(line.split("\\W+")))
.filter(word -> !word.matches("\\d+"))
.map(String::trim)
.collect(Collectors.toCollection(TreeSet::new));
System.out.println(words);
/*
* [Have, Hello, Saturday, This, Today, a, day, digit, is, nice, world]
*/
}
}
Among them FileToWordsBuilder.txt
Sentence content below:
Hello world!
Today is Saturday?
Have a nice day!
This is digit 666.
Files.lines()
opens the file pointed to by Path
and turns it into a line of text Stream
. The next line of code splits these lines of text by one or more non-word characters (\\W+
), where the resulting array becomes Arrays.stream()
a>. to remove any surrounding whitespace that may be present, and finally place the words into a that are all numbers. Next use will find and delete composed of words. Stream
, and then the result is expanded and mapped back to a Stream
matches(\\d+)
String
String.trim()
TreeSet
5.4 Combine all flow elements
reduce(BinaryOperator)
: UseBinaryOperator
to combine all stream elements. Because this stream may be empty, aOptional
is returned.reduce(identity, BinaryOperator)
: Same as above, but usingidentity
as the initial value for this combination, so even if the stream is empty, we still getidentity
as a result.reduce(identity, BiFunction, BinaryOperator)
: This one is more complex (so we won't cover it), but it's listed here because it may be more efficient. This requirement can be expressed more simply by combining explicitmap()
andreduce()
operations.
Let’s take a look at the simplest usage:
package stream;
import java.util.Random;
import java.util.stream.Stream;
class Apple {
private final int price;
Apple(int price) {
this.price = price; }
int getPrice() {
return price; }
@Override
public String toString() {
return "Apple(" + price + ")";
}
// 生成器
static Random rand = new Random(47);
static Apple generator() {
return new Apple(rand.nextInt(100));
}
}
public class Reduce {
public static void main(String[] args) {
Stream.generate(Apple::generator)
.limit(5)
.peek(a -> System.out.println("Peek: " + a))
.reduce((a0, a1) -> a0.getPrice() > 60 ? a0 : a1)
.ifPresent(System.out::println);
/*
* Peek: Apple(58)
* Peek: Apple(55)
* Peek: Apple(93)
* Peek: Apple(61)
* Peek: Apple(61)
* Apple(93)
*/
}
}
When we use reduce()
, we do not provide the first parameter as the initial value, which means Then it will generate a Optional
, and only when the result is not empty
, the Optional.ifPresent()
method will call Consumer<Apple>
(The reason why System.out::println
is consistent is that it can convert into a < through the toString()
method a i=11>). Apple
String
The first parameter in the Lambda expressiona0
is the result returned when this reduce()
was called last time, and the second parameter, which is the sequence the next element in . , otherwise accept is greater than 60, accept 's a1
is the new value from the stream. If a0
price
a0
a1
As a result, what we get is the firstprice
in the streamApple
greater than 60. Once such an object is found, it will Hold on to it, even if other candidates appear.