当前位置:网站首页>Stream programming: stream support, creation, intermediate operation and terminal operation
Stream programming: stream support, creation, intermediate operation and terminal operation
2022-06-21 09:35:00 【tragically unhappy】
Stream programming
Preface
Collections optimize the storage of objects , And flow (Streams) It is about the processing of a group of objects .
A stream is a sequence of elements independent of any particular storage mechanism —— actually , We say that streams have no storage .
- Instead of iterating through a set (Iterator) Element approach , Use stream (Streams) It can be downloaded from Extract elements from the pipeline and manipulate them . These pipes are usually connected in series to form a complete set of pipelines , And the stream operates on them .
- Most of the time , You store objects in collections to handle them , So when you program, you will find that your main focus will shift from collections to streams .
- One of the core benefits of flow is : It makes your program shorter and easier to understand . When will Lambda When expressions and method references are used with streams, you will find that they are self-contained . Flow makes Java 8 Attraction of machines and tools ( It's really great ).
for instance , If you want to show it randomly 5-20 Between non repeating integers (4 Conditions ).
Your idea may have been like this at first , First focus on which ordered set to use , Then, follow-up operations are carried out around this set . But using streaming programming , You can do that :
public class Randoms {
public static void main(String[] args) {
new Random(47)
.ints(5, 20)
.distinct()
.limit(7)
.sorted()
.forEach(System.out::println);
}
}
/* output: 6 10 13 16 17 18 19 */
This is really too simple , We just need to simply state what we want to do .
First we give Random Object a seed value , ints() Method generates a stream and ints() Methods can be overloaded in many ways —– Two parameters define the boundary of the resulting value . This will produce a stream of random integers . Then we use the stream Intermediate operation distinct() Make the elements in the flow not repeated , then limit() Take the first seven elements . Then sort and iterate through the output ,forEach() The operation is performed on each object in the stream according to the function passed to it .
Be careful : No variables are declared in the above example . Streams can be used without assignment or mutable data , Modeling existing systems , This is very useful .
What we did before was Command programming In the form of ( Indicate how to do each step ), And now we use Declarative programming (Declarative Programming)—— It states what to do , Instead of pointing out how to do .
public class ImperativeRandoms {
public static void main(String[] args) {
Random rd = new Random(47);
SortedSet<Integer> rints = new TreeSet<>();
while (rints.size() < 7) {
int r = rd.nextInt(20);
if (r < 5) {
// If the conditions are met , Just jump out of this cycle
continue;
}
rints.add(r);
}
System.out.println(rints);
}
}
/* output: [7, 8, 9, 11, 15, 16, 18] */
We should clearly see the difference between the two codes . stay Randoms.java in , We don't have to define any variables , But in this example we define 3 individual , And the code is more complex , And nextInt() Method has no lower bound , The efficiency is not high .
most important of all : You have to study the code to see what this example is doing , And in the Random.java in , The code will tell you what it is doing .
Like in ImperativeRandoms.java The way in which the iteration process is written as shown in is called External iteration . And in the Random.java Is an internal iteration , This is a The core features of streaming programming . Internal iterations can produce more readable code , And it's easier to use multi-core processors . By giving up control of the iterative process , We can give control to parallel The mechanism of Urbanization .
Another important aspect : Streams are lazy loaded . This means that it will only be calculated when absolutely necessary . You can think of flow as “ Delay list ”, Because of the computation delay , Flow enables us to express very large ( Even unlimited ) Sequence , Without thinking about memory .
One 、 Stream support
Java Designers face such a difficult problem : How to integrate a new concept of flow into the existing class library ? By adding more methods to the previous class library , As long as we don't change the original method , Existing code will not be disturbed .
The second problem is the use of Interface Class library of . If you add a new method to the interface , Then all the class libraries that implement this interface have to be changed .Java 8 The solution is : Add the interface default The method of decoration . So you will find some in some stream interfaces default Method , This method does not require subclasses to be implemented separately , Its preset operation has met almost all our usual needs . Through this program , Designers can smoothly embed streaming methods into existing classes .
There are three types of stream operations : Create stream , Modify stream elements ( Intermediate operation ), Consumption stream elements ( Terminal operation ). The last type usually means collecting stream elements ( Import the finished stream into a collection ).
Two 、 Stream creation
public class StreamOf {
public static void main(String[] args) {
Stream.of(new Bubble(1), new Bubble(2), new Bubble(3))
.forEach(System.out::println);
Stream.of("It's ", "a ", "wonderful ", "day ", "for ", "pie!")
.forEach(System.out::print);
System.out.println();
Stream.of(3.1415926, 2.718, 1.618)
.forEach(System.out::println);
System.out.println("=====================================");
List<Bubble> bubbles = Arrays.asList(new Bubble(3), new Bubble(4), new Bubble(5));
System.out.println(bubbles.stream()
.mapToInt(b -> b.i)
.sum());
Set<String> w = new HashSet<>(Arrays.asList("It's a wonderful day for pie!".split(" ")));
w.stream()
.map(x -> x + " ")
.forEach(System.out::print);
System.out.println();
Map<String, Double> m = new HashMap<>();
m.put("pi", 3.14159);
m.put("e", 2.718);
m.put("phi", 1.618);
m.entrySet()
.stream()
.map(e->e.getKey() + ":" + e.getValue())
.forEach(System.out::println);
}
}
/* output: Bubble{i=1} Bubble{i=2} Bubble{i=3} It's a wonderful day for pie! 3.1415926 2.718 1.618 ===================================== 12 a pie! It's for wonderful day phi:1.618 e:2.718 pi:3.14159 */
We can go through Stream.of() It's easy to turn a set of elements into a stream . besides , Each collection can be created by calling stream() Method to generate a stream .
- Creating
List<Bubble>After object , All we need to do is call thestream(). - Intermediate operation
map()Gets all the elements in the stream , And the element in the stream is operated to create a new element , Then it is transferred to the reflux . Usuallymap()Gets the object and creates a new object , But here comes a special For numeric types The flow of . for example :mapToInt()Method converts a stream of objects to a stream containing integer numbers IntStream. - In order to learn from Map Stream data is generated in the collection , We call
entrySet()Generate an object stream , Each object contains a key Key and its associated value value . Then call... SeparatelygetKey()andgetValue().
2.1 Random number stream
public class RandomGenerators {
public static <T> void show(Stream<T> stream) {
stream
.limit(4)
.forEach(System.out::println);
System.out.println("+++++++++++++++");
}
public static void main(String[] args) {
Random rd = new Random(47);
/* Do not control the upper and lower limits , without show Methods limit, There will be infinite IntStream The value in . here boxed Method has generated a stream before , It is only a basic data type ( Such as IntStream), and boxed You can wrap basic data types (Stream<Integer>) */
show(rd.ints().boxed());
show(rd.longs().boxed());
show(rd.doubles().boxed());
// Control the upper and lower limits
show(rd.ints(10, 20).boxed());
show(rd.longs(50, 100).boxed());
show(rd.doubles(20, 30).boxed());
// Control flow size
show(rd.ints(2).boxed());
show(rd.longs(2).boxed());
show(rd.doubles(2).boxed());
// Control the size and limits of the flow
show(rd.ints(2, 10, 20).boxed());
show(rd.longs(2, 50, 100).boxed());
show(rd.doubles(2, 20, 30).boxed());
}
}
/* output: -1172028779 1717241110 -2014573909 229403722 +++++++++++++++ 2955289354441303771 3476817843704654257 -8917117694134521474 4941259272818818752 +++++++++++++++ 0.2613610344283964 0.0508673570556899 0.8037155449603999 0.7620665811558285 +++++++++++++++ 16 10 11 12 +++++++++++++++ 65 99 54 58 +++++++++++++++ 29.86777681078574 24.83968447804611 20.09247112332014 24.046793846338723 +++++++++++++++ 1169976606 1947946283 +++++++++++++++ 2970202997824602425 -2325326920272830366 +++++++++++++++ 0.7024254510631527 0.6648552384607359 +++++++++++++++ 17 18 +++++++++++++++ 81 86 +++++++++++++++ 21.898377705316413 23.22662025293785 +++++++++++++++ */
To eliminate redundant code , Generic methods are used here to create different types of flows . Actually here Random class Only basic types of streams can be generated , however :boxed() The stream operation automatically wraps the base type into the corresponding boxing type , Thus making show() Method can call .
We can use Random Create... For any collection of objects Supplier.
public class RandomWords implements Supplier<String> {
List<String> words = new ArrayList<>();
Random rand = new Random(47);
RandomWords(String fname) throws IOException {
List<String> lines = Files.readAllLines(Paths.get(fname));
// Skip the first line
for (String line : lines.subList(1, lines.size())) {
for (String word : line.split("[ .?,]+")) {
words.add(word);
}
}
}
@Override
public String get() {
return words.get(rand.nextInt(words.size()));
}
public static void main(String[] args) throws IOException {
System.out.println(
Stream.generate(new RandomWords("C:\\Users\\gt136\\Downloads\\Documents\\cheese.txt"))
.limit(10)
.collect(Collectors.joining(" ")));
}
}
/* cheese.txt // streams/Cheese.dat Not much of a cheese shop really, is it? Finest in the district, sir. And what leads you to that conclusion? Well, it's so clean. It's certainly uncontaminated by cheese. output: it shop sir the much cheese by conclusion district is */
- You can see it here
split()More complex applications . In the constructor : Every line is split Methods are separated by spaces within square brackets or other punctuation marks . After square brackets+The sign means that something in front of it can appear once or more . collect()The operation combines all the flow elements according to the parameters . When you useCollectors.joining()As its parameter , Will get a String Result of type : That is, all elements in the stream arejoining()The parameters in are separated .generate()Method can put anySupplier<T>Used to generate T Unordered flow of type , It's rewritten get() The method is collect Method to call .
2.2 int Type of range
public class Range {
// Generate from start-end Step by step step A growing sequence
public static int[] range(int start, int end, int step) {
if (step == 0) {
throw new IllegalArgumentException("Step cannot be zero!");
}
// Operating range
int sz = Math.max(0,step >= 0 ? (end + step -1 -start) / step : (end + step + 1 -start) / step);
int[] result = new int[sz];
for (int i = 0; i < sz; i++) {
result[i] = start + (i * step);
}
return result;
}
// Generate from start To end In turn, increasing 1 Sequence
public static int[] range(int start, int end) {
return range(start, end, 1);
}
// Generate from 0 To n In turn, increasing 1 Sequence
public static int[] range(int n) {
return range(0, n);
}
}
IntStream Class provides range() Method is used to generate an integer :
import static java.util.stream.IntStream.*;
public class Ranges {
public static void repeat(int n, Runnable action) {
range(0, n).forEach(i->action.run());
}
public static void main(String[] args) {
// traditional method
int result = 0;
for (int i = 10; i < 20; i++) {
result += i;
}
System.out.println(result);
//for-in loop
result = 0;
for (int i : range(10, 20).toArray()) {
result += i;
}
System.out.println(result);
// Use stream
System.out.println(range(10,20).sum());
// Use stream but not applicable IntStream The method in
System.out.println(new Random()
.ints(10, 20)
.limit(5).sum());
repeat(3,()-> System.out.println("Looping!"));
}
}
The first way in the main method is that we traditionally write for The way of circulation ; The second way is to use range() Method ( The package was introduced in advance ) Create a stream and convert it to an array , And then in for-in Used in code blocks . But the third is to use streams entirely .
Be careful :IntStream.range() Compared with the previous Range.range() Subject to many restrictions . This is due to its optional third parameter , The latter allows steps greater than one ( The former is fixed 1), And can be generated from large to small .
2.3 generate() Method can put any Supplier<T> Used to generate T Unordered flow of type
public class Generator implements Supplier<String> {
Random rand = new Random(47);
char[] letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
@Override
public String get() {
return "" + letters[rand.nextInt(letters.length)];
}
public static void main(String[] args) {
String word = Stream.generate(new Generator())
.limit(30)
.collect(Collectors.joining());
System.out.println(word);
// Can produce T Object contains a stream of the same data ( Because there is only one value )
Stream.generate(() -> "duplicate")
.limit(3)
.forEach(System.out::println);
Stream.generate(Bubble::bubbler)
.limit(5)
.forEach(System.out::println);
}
}
/* output: YNZBRNYGCFOWZNTCQRGSEGZMMJMROE duplicate duplicate duplicate Bubble{i=0} Bubble{i=1} Bubble{i=2} Bubble{i=3} Bubble{i=4} */
reference RandomWords.java Medium Stream.generate() collocation Supplier<T> Usage of , Remember that it can generate any T Types of flow , The same is collect Called get() Method .
Just to summarize :new Random().ints() Can produce a random sequence of values , and Stream.generate() Then a random sequence can be generated in the provided set or object .
If you want to create a stream that contains the same object , Just pass in a lambda To generate() in , Just like the last expression above .
2.4 iterate()
Stream.iterate() The first element of the resulting stream is the seed (iterate The first parameter of the method ), Then pass the seed to the method (iterate The second argument to the method ). The result of the method run is added to the stream ( As the next element of the stream ), And stored , As the next call iterate() The first parameter of the method , And so on . We can use it to generate a Fibonacci sequence .
public class Fibonacci {
int x = 1;
Stream<Integer> number() {
return Stream.iterate(0, i -> {
int result = x + i;
x = i;
return result;
});
}
public static void main(String[] args) {
new Fibonacci().number()
.skip(20)// Before filtration 20 individual
.limit(10) // And then take 10 individual
.forEach(System.out::println);
}
}
/* output: 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 */
3、 ... and 、 The builder mode of flow
stay Builder pattern (Builder design pattern) in , First ① Create a builder object , then ② Pass multiple pieces of information needed to create the flow to it , Last ③** builder Object to perform “ establish ” flow ** The operation of .Stream The library provides Builder, Here is the code to rewrite the above example :
public class FileToWordsBuilder {
//builder Method returns a Builder Implementation class of
Stream.Builder<String> builder = Stream.builder();
public FileToWordsBuilder(String filePath) throws IOException {
Files.lines(Paths.get(filePath))
.skip(1)// Skip the first line
.forEach(line->{
for (String w : line.split("[ .?,]+")) {
builder.add(w);
}
});
}
Stream<String> stream() {
return builder.build();
}
public static void main(String[] args) throws IOException {
new FileToWordsBuilder("cheese.txt")
.stream()
.limit(7)
.map(w -> w + " ")
.forEach(System.out::print);
}
}
/* output: Not much of a cheese shop really */
Be careful : The constructor adds all the words in the file ( Except for the first line ), however , No call builder(). As long as you don't call stream() Method , You can go on to builder Object .
In the more complete form of the class , You can add a flag bit to view build() Is it called , And if possible, add a way to add more words . stay Stream.builder call build() Method and continue to try to add words will produce an exception .
3.1 Arrays
Arrays Class contains a named stream() A static method for converting an array to a stream . The main method is used to create a stream , And will excute() Apply to each element .
public interface Operations {
void execute();
static void runOps(Operations... ops) {
for(Operations op : ops)
op.execute();
}
static void show(String msg) {
System.out.println(msg);
}
}
/*************************************/
public class Machine2 {
public static void main(String[] args) {
Arrays.stream(new Operations[]{
() -> Operations.show("Bing"),
() -> Operations.show("Crack"),
() -> Operations.show("Twist"),
() -> Operations.show("Pop")
}).forEach(Operations::execute);
}
}
/* output: Bing Crack Twist Pop */
new Operations[] Dynamically created Operations An array of objects .
stream() The same can produce IntStream, LongStream and DoubleStream.
public class ArrayStreams {
public static void main(String[] args) {
Arrays.stream(new double[]{
3.14159, 2.718, 1.618 })
.forEach(n->System.out.format("%f ", n));
System.out.println();
Arrays.stream(new int[]{
1, 3, 5})
.forEach(n -> System.out.format("%d ", n));
System.out.println();
Arrays.stream(new long[]{
11, 22, 33, 44, 66})
.forEach(n -> System.out.format("%d ", n));
System.out.println();
// Select a subdomain
Arrays.stream(new int[]{
1, 3, 5, 7, 8, 10, 12}, 3, 6)
.forEach(n -> System.out.format("%d ", n));
}
}
/* output: 3.141590 2.718000 1.618000 1 3 5 11 22 33 44 66 7 8 10 */
the last one stream() The call to the method of has two additional parameters , The first parameter tells stream() Select the element from that position in the array , The second parameter is the stop position .
3.2 Regular expressions
Java 8 stay java.util.regex.Pattern A new method has been added to splitAsStream(). This method can convert a sequence of characters into a stream according to the formula passed in . But there's a limit : The input can only be CharSequence, So you can't take a stream as its parameter .
public class FileToWordsRegexp {
private String all;
public FileToWordsRegexp(String filepath) throws IOException {
this.all = Files.lines(Paths.get(filepath))
.skip(1)
.collect(Collectors.joining(" "));
}
public Stream<String> stream() {
return Pattern
.compile("[ .,?]+")
.splitAsStream(all);//splitAsStream() receive calls only charSequence object , however String Meet this requirement
}
public static void main(String[] args) throws IOException {
FileToWordsRegexp fw = new FileToWordsRegexp("cheese.txt");
fw.stream()
.limit(7)
.map(m -> m + " ")
.forEach(System.out::print);
fw.stream()
.skip(7)
.limit(2)
.map(x -> x + " ")
.forEach(System.out::print);
}
}
/*output: Not much of a cheese shop really is it */
This time we use the stream to convert the file to a string , Then use regular expressions to turn the string into a stream of words .
Everything in the file is read in the constructor , When calling stream() When , You can get a stream as usual , but This time we can call stream(), Each time a new stream is created from the stored string . There is a limitation , The whole file must be stored in memory ; Many times this is not a problem , But this misses the important advantage of streaming operations :
- “ There is no need to store the stream .” Of course , Streams do require some memory storage , But only a small part of the sequence is stored , Not the whole sequence .
- They're lazy load calculations
We'll talk about solutions later . It's just 4.5
Four 、 Intermediate operation
Intermediate operations are used to get objects from a stream , And output the object as another stream from the back end , To connect to other places .
4.1 Tracking and debugging
peek() The purpose of the operation is to help debug . It allows you to view elements in the stream without modification .
public class Peeking {
public static void main(String[] args) throws IOException {
FileToWordsBuilder fw = new FileToWordsBuilder("C:\\Users\\gt136\\Downloads\\Documents\\cheese.txt");
fw.stream()
.skip(21)
.limit(4)
.map(w -> w + " ")
.peek(System.out::print)
.map(String::toUpperCase)
.peek(System.out::print)
.map(String::toLowerCase)
.forEach(System.out::print);
}
}
/* output: Well WELL well it's IT'S it's so SO so clean CLEAN clean */
At first, I was confused about the printing of this stream , If you step through the execution of this flow , You will find the characteristics of declarative programming : It is all executed according to the declaration , Finally, the flow you need is presented to you , And you can't see the middle operation .
4.2 Stream element ordering
fw2.stream()
.skip(10)
.limit(10)
.sorted(Comparator.reverseOrder())
.map(w -> w + " ")
.forEach(System.out::print);
We met before sorted() Method , But here is another form of implementation : Passed in a Comparator Parameters .
4.3 Remove elements
public class Prime {
public static Boolean isPrime(long n) {
return rangeClosed(2, (long) Math.sqrt(n))// Generate a sequence that increases by one from the first parameter to the second parameter , Contains the initial value and upper limit value
.noneMatch(i -> n % i == 0);
}
public static LongStream numbers() {
return iterate(2, i -> i + 1)// Produce a result from 2 The beginning of an infinite sequence
.filter(Prime::isPrime);
}
public static void main(String[] args) {
new Prime().numbers()
.limit(10)
.forEach(n -> System.out.format("%d ", n));
System.out.println();
new Prime().numbers()
.skip(90)
.limit(10)
.forEach(n-> System.out.format("%d ",n));
}
}
/* output: 2 3 5 7 11 13 17 19 23 29 467 479 487 491 499 503 509 521 523 541 */
distinct(): Can eliminate duplicate elements , Instead of creating a Set To deal with it , This method is much simpler .filter(Predicate): Filtering operation , Keep the following elements : If the element is passed to the filter function, the result is true .
In the above code isPrime() Is used to detect prime numbers ,rangeClosed(long startInclusive, final long endInclusive) Contains the initial and upper values . This function means that if you can't divide by an integer , be noneMatch() return true, If 0, return false, And this method will exit in case of any error .
4.4 Apply functions to elements
map(Function): Apply function operations to the elements of the input stream , And pass the return value to the output stream .mapToInt(ToIntFuntion): Ibid , But the result is IntStream .mapToLong(ToLongFunction): Ibid , But the result is LongStream.mapToDouble(ToDoubleFunction): Ibid , But the result is DoubleStream.
public class FunctionMap {
static String[] elements = {
"12", "", "23", "34"};
// Convert an array to a stream
static Stream<String> testStream() {
return Arrays.stream(elements);
}
static void test(String descr, Function<String, String> func) {
System.out.println("---( " + descr + ")---");
testStream()
.map(func)
.forEach(System.out::println);
}
public static void main(String[] args) {
// Make the original data into the form we want
test("add brackets", s -> "[" + s + "]");
test("Increment",s->{
try {
return Integer.parseInt(s) + 1 + "";
} catch (NumberFormatException e) {
return s;
}
});
}
}
/* output: ---( add brackets)--- [12] [] [23] [34] ---( Increment)--- 13 24 35 */
In the self incrementing example above , We convert a string to an integer , If it cannot be converted to an integer, an exception will be thrown , At this point, put the original string into the output stream .map() Mapping one string to another , however We can produce and accept completely different types , Thus changing the data type of the stream . You only need to process the single data in the stream , such as :.map(Numbered::new),Numbered The constructor of receives elements .
4.5 stay map() Combinatorial flow in water
Suppose you now have an incoming element stream , And we're going to use map() function , But the problem is , The function of these functions is to generate a stream : We want to create a flow of elements , In fact, there is a flow of element flow ! Please note that : The operation here is for the incoming Flow, not front The function type in the example
flatMap(): Did two things : Apply the stream generating function to each element ( Andmap()It's the same thing ), Then flatten each stream into elements , So what it ultimately produces is just elements .flatMap(Function): When Function Use when generating streams .flatMapToInt(Function): When Function produce IntStream When using .flatMapToLong(Function): When Function produce LongStream When using .flatMapToDouble(Function): When Function produce DoubleStream When using .
To figure out how it works , Pass a deliberately designed function from to map() Start .
/** * Stream in stream Among them map Method :public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {} * This map The parameter in is Function: That is to say, it will call apply Method , This is realized by ourselves ; and Function Parameters in , The first one refers to the type of input stream , * That is, if you are the type of flow Integer type , So this is Integer type ; The second parameter refers to the type of new stream you return , This can also be defined by itself . * @Date 2021/6/17 16:41 * @Created by gt136 */
public class StreamOfStreams {
public static void main(String[] args) {
Stream.of(1,2,3)
/*.map(new Function<Integer, Stream<String>>() { @Override public Stream<String> apply(Integer integer) { return Stream.of("null"); } })*/
.map(i->Stream.of("Gonzo","Kermit","Breaker"))
.map(e->e.getClass().getName())
.forEach(System.out::println);
}
}
/* output: java.util.stream.ReferencePipeline$Head java.util.stream.ReferencePipeline$Head java.util.stream.ReferencePipeline$Head */
We hope to get the character stream , But what we actually get is “Head” Flow of flow . We can use flatMap() To solve
Stream.of(1, 2, 3)
.flatMap(i -> Stream.of("Gonzo", "Kermit", "Breaker"))
.forEach(System.out::println);
/* output: Gonzo Kermit Breaker Gonzo Kermit Breaker Gonzo Kermit Breaker */
Here, each element stream returned from the mapping is transformed into elements .
Here's another demonstration , Start with an integer stream , Then use each integer element to create more random numbers .
Stream.of(1, 2, 3, 4, 5)
.flatMapToInt(i -> IntStream.concat(
rand.ints(0, 100).limit(i), IntStream.of(-1)))
.forEach(n -> System.out.format("%d ", n));
/*output: 58 -1 55 93 -1 61 61 29 -1 68 0 22 7 -1 88 28 51 89 9 -1 */
Introduced here concat(), It combines two streams in parameter order .
Consider again the previous task of dividing the file into word streams . The last thing to use is FileToWordsRegexp.java, The problem with it is that you need to read the entire file into the line list — The list needs to be stored .
What we need is to create a word stream that does not need an intermediate storage layer :
public class FileToWords {
public static Stream<String> stream(String filePath) throws IOException {
return Files.lines(Paths.get(filePath))
.skip(1)
.flatMap(line -> Pattern.compile("\\W+").splitAsStream(line));
}
}
stream() Now it's a static method , Because it can complete the whole flow creation process by itself .
Be careful :\\W+ Is a regular expression . Express “ Nonword character ”, In lowercase form w Express “ Word characters ”.
In the above code Pattern.compile().splitAsStream() The result is flow , That means when we just want a simple word stream , Call... On the incoming rowstream map() It will produce a stream of words . So the solution is : Use flatMap() Flatten the flow of element flow into a simple element flow , perhaps , We can use String.split() Generate an array , It can be Arrays.stream() Turn into a stream .
Because with real flow , Not a stream of streams , So every time you need a new stream , We all have to start from scratch , Because streams cannot be reused .
Because we may encounter when operating the flow “ Air flow “, The way to solve empty flow is to use Optional class . So if you are interested here, you can read this article Optional Class uses
5、 ... and 、 Terminal operation
The following operation will get the final result of the stream . At this point, we can't go back and forth . so to speak : Terminal operation is the last thing we can do in the flow pipeline .
5.1 Array
toArray(): Convert the stream to an array of the appropriate type .toArray(generator): In special circumstances , Generate arrays of custom types .
When we need data of array type to facilitate subsequent operations , The above method is very useful .
public class RandInts {
private static int[] rints = new Random(47)
.ints(0, 1000)
.limit(100)
.toArray();// Convert the stream to an array
public static IntStream rands() {
return Arrays.stream(rints);// Converting arrays to streams
}
}
The above example will 100 A number in the range of 0 - 1000 The random number stream between is converted into an array and stored in rints in . thus , Every time you call rands() You can get the same integer stream repeatedly when you use it .
5.2 loop
forEach(Consumer): Common as :System.out::printlnAs Consumer function .forEachOrdered(Consumer): GuaranteeforEachOperate in the original stream order .
The first form : Unordered operation , It makes sense only when parallel flows are introduced , Here is a brief introduction parallel(): It can realize multi processor parallel operation , The implementation principle is to divide the flow into multiple ( Usually the number is CPU The core number ) And perform operations on different processors . Because we are taking an internal iteration , So this can be achieved .
parallel() It seems simple , It's really tricky . More will be sorted out in concurrent programming .
Let's introduce parallel() To help understand forEachOrdered(Consumer) The role and usage scenarios of .
public class ForEach {
static final int SZ = 14;
public static void main(String[] args) {
rands().limit(SZ)
.forEach(value -> System.out.format("%d ", value));
System.out.println(
);
rands().limit(SZ)
.parallel()
.forEach(n -> System.out.format("%d ", n));
System.out.println();
rands().limit(SZ)
.parallel()
.forEachOrdered(n -> System.out.format("%d ", n));
}
}
/* output: 258 555 693 861 961 429 868 200 522 207 288 128 551 589 551 589 861 288 555 868 693 207 128 200 961 429 258 522 258 555 693 861 961 429 868 200 522 207 288 128 551 589 */
In the first stream , not used parallel(), So take the element from rands() Output the results in sequence . In the second stream , introduce parallel(), Even if the flow is small , The output results are also different , This is the result of multiprocessor parallel operation , If you run it several times , You will find that the output is different .
In the last stream , Use at the same time parallel() and forEachOrdered(Consumer) To force the original stream data to be preserved . Therefore, the use of the latter method has no effect on non parallel flows .
5.3 aggregate
collect(Collector): Use Collector Collect stream elements into the result set .collect(Supplier,BiConsumer,BiConsumer): ditto , The first parameter creates a new result set , The second parameter collects the next element into the result set , The third parameter is used to combine the two results .
Suppose that our current requirement is to ensure that the elements are in order , Store elements in TreeSet in . although Collectors There's no specific toTreeSet(), But we can do this by passing the constructor reference of the collection to Collectors.toCollection(), To build any type of collection .
public class TreeSetOfWords {
public static void main(String[] args) throws IOException {
Set<String> words2 = Files.lines(Paths.get("C:\\demo\\src\\main\\com\\thingInJava\\streamsProgram\\optional\\TreeSetOfWords.java"))
.flatMap(s-> Arrays.stream(s.split("\\W+")))//split Will be based on the parameters String Cut to array ,
/*.flatMap(new Function<String, Stream<String>>() { @Override public Stream<String> apply(String s) { System.out.println(s); return Arrays.stream(s.split("\\W+")); } })*/
.filter(s->!s.matches("\\d+"))
.map(String::trim)// Remove... From the string “ ”
.filter(s->s.length()>2)
.limit(100)
.collect(Collectors.toCollection(TreeSet::new));
System.out.println(words2);
}
}
/* output: [Arrays, Collectors, Downloads, Files, IOException, Paths, Set, String, System, TreeSet, TreeSetOfWords, Users, args, class, collect, com, demo, file, filter, flatMap, get, import, java, length, limit, lines, main, map, matches, new, nio, optional, out, package, println, public, split, src, static, stream, streamsProgram, thingInJava, throws, toCollection, trim, util, void, words2] */
The whole execution process is :Files.lines() open Path And convert it into a stream of rows . The next line of code takes a line from the stream collection (String) Segmentation ( Array ) And rewritten into a stream , Last flatMap Multiple word streams formed from each line , Flat mapping into a word stream . The next line removes all numeric strings , then trim Remove the space around the word ,filter Method to filter all items with a length less than 3 's words , And take the front of the final result 100 Save them in TreeSet in .
We can also generate in the stream Map.
class Pair{
public final Character c;
public final Integer i;
public Pair(Character c, Integer i) {
this.c = c;
this.i = i;
}
@Override
public String toString() {
return "Pair{" +
"c=" + c +
", i=" + i +
'}';
}
public Character getC() {
return c;
}
public Integer getI() {
return i;
}
}
class RandomPair{
Random rand = new Random(47);
// Infinite iterators for random upper case letters , call iterator Method will return the iterator of the stream element ; Here is the iterator of these upper case child parent streams
Iterator<Character> captures = rand.ints(65, 91)
.mapToObj(i -> (char) i)
.iterator();
public Stream<Pair> stream() {
return rand.ints(100, 1000)
.distinct()
// Because the above is random int Type of flow and de duplication , therefore , Corresponding mapToObject The internal parameter of is IntFunction<Object>(),
// Its default method is public Object apply(int value) {return null;}; Here we will Object Change to what we need Pair type
.mapToObj(i -> new Pair(captures.next(), i));
}
}
public class MapCollector {
public static void main(String[] args) {
Map<Integer, Character> map = new RandomPair().stream()
.limit(8)
.collect(Collectors.toMap(Pair::getI, Pair::getC));
System.out.println(map);
}
}
/* output: {688=W, 309=C, 293=B, 761=N, 858=N, 668=G, 622=F, 751=N} */
RandomPair Created randomly generated Pair Object flow , stay Java in , We can't just combine two streams in some way . So here we create a stream of integers , And use mapToObj() Convert the integer stream to Pair flow .capChars The random uppercase iterator of creates a stream , then next() So that we can stream() This stream is used in . As far as the author knows , This is the only way to combine multiple streams into a new object stream .
ad locum , We only use the simplest form of Collectors.toMap(), This method only needs two functions to get the key and value from the stream . There are other forms of overloading , One is when a key conflict occurs , Use a function to handle conflicts .
Most of the time ,java.util.stream.Collectors Preset Collector Can meet our requirements . besides , You can also use the second form of collect().
public class SpecialCollector {
public static void main(String[] args) throws IOException {
ArrayList<String> words = FileToWords.stream("C:\\Users\\gt136\\Downloads\\Documents\\cheese.txt")
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
words.stream()
.filter(s->s.equals("cheese"))
.forEach(System.out::println);
}
}
/* output: cheese cheese */
5.4 Combine
reduce(BinaryOperator): Use BinaryOperator To combine elements in all streams . Because the stream may be empty , Its return value is Optional.reduce(identity,BinaryOperator): The function is the same as above , But use identity As the initial value of its combination . Because if the flow is empty ,identity It's the result .reduce(identity,BFunction,BinaryOperator): More complex forms of use , It's included here , Because it can improve efficiency . Usually , We can explicitly combinemap()andreduce()To express it more simply .
class Frobnitz {
int size;
Frobnitz(int size){
this.size = size;
}
@Override
public String toString() {
return "Frobnitz{" +
"size=" + size +
'}';
}
static Random rand = new Random(47);
static final int BOUND = 100;
// Random generation 100 Inside
static Frobnitz supply() {
return new Frobnitz(rand.nextInt(BOUND));
}
}
public class Reduce {
public static void main(String[] args) {
Stream.generate(Frobnitz::supply)
.limit(10)
.peek(System.out::println)
//reduce The function of the method is to traverse the elements in the stream once , For the first time, the first parameter is null, The second parameter is the first stream element , The next first parameter is the element after the last operation , The second parameter is the next stream element
.reduce((fr0, fr1) -> fr0.size < 50 ? fr0 : fr1)
.ifPresent(System.out::println);// To 29 The above method was not satisfied before , So values are constantly being replaced , After that, it will always be less than 50 Of 29
}
}
/* output: Frobnitz{size=58} Frobnitz{size=55} Frobnitz{size=93} Frobnitz{size=61} Frobnitz{size=61} Frobnitz{size=29} Frobnitz{size=68} Frobnitz{size=0} Frobnitz{size=22} Frobnitz{size=7} Frobnitz{size=29} */
because supply() Method as a Supplier It's signature compatible , We can supply Method is passed to as a method reference Stream.generate()( This signature is called structural consistency ). Have we used “ Initial value ” As the first parameter reduce() Method , So the result is Optional type .Optional.ifPresent() Method is called only if the result is not empty Consumer<>(println Methods can be called because Frobnitz Can pass toString Method to String).
Lambda The first argument to the expression fr0 yes reduce() Is the result of the last call ,fr1 Is the value passed from the stream .reduce() Medium Lambda The expression uses a ternary expression , When fr0 Of size Less than 50 when , take fr0 As a result , Otherwise... In the sequence fr1 As a result . When you get the first size Less than 50 Of Frobnitz, As long as this result is obtained, other elements in the stream will be ignored , This is a very strange limitation .
5.5 lookup
findFirst(): Returns the... Of the first stream element Optional, Return if the stream is empty Optional.empty.findAny(): Returns the... Containing any stream element Optional, Return if the stream is empty Optional.empty.
import static com.gui.demo.thingInJava.streamsProgram.optional.RandInts.*;
public class SelectElement {
public static void main(String[] args) {
//rands Method to convert an array into a stream ,findFirst
System.out.println(rands().findFirst().getAsInt());
System.out.println(rands().parallel().findFirst().getAsInt());
System.out.println(rands().findAny().getAsInt());
System.out.println(rands().parallel().findAny().getAsInt());
}
}
/* output: 258 258 258 242 */
Whether the stream is parallelized or not ,findFirst() Always select the first element in the stream . For nonparallel streams ,findAny() The first element in the stream is selected ( Even if, by definition, any element is selected ). In this case , use parallel() Parallelize streams , To show findAny() The possibility of not selecting the first element of the stream .
If it is necessary to select the last element in the stream , Use it reduce(). The code is as follows :
public class LastElement {
public static void main(String[] args) {
OptionalInt last = IntStream.range(10, 20)
.reduce((n1, n2) -> n2);
System.out.println(last.orElse(-1));
Optional<String> lastobj = Stream.of("one", "two", "three")
.reduce((n1, n2) -> n2);
System.out.println(lastobj.orElse("Nothing there"));
}
}
/* output: 19 three */
reduce() The argument to is just to replace the last two elements with the last one , In the end, only the last element is generated . If it is a digital stream , You have to use similar numbers Optional type , Otherwise use Optional type , Like in the example above Optional<String>.
5.6 Information
count(): The number of elements in the stream .max(Comparator): Based on the incoming Comparator Determined by “ Maximum ” Elements .min(Comparator): Based on the incoming Comparator Determined by “ Minimum ” Elements .
String The type has a preset Comparator Realization .
public class Informational {
public static void main(String[] args) throws IOException {
System.out.println(FileToWords.stream("C:\\Users\\gt136\\Downloads\\Documents\\cheese.txt").count());
System.out.println(FileToWords.stream("C:\\Users\\gt136\\Downloads\\Documents\\cheese.txt")
.min(String.CASE_INSENSITIVE_ORDER)
.orElse("NONE"));
System.out.println(FileToWords.stream("C:\\Users\\gt136\\Downloads\\Documents\\cheese.txt")
.max(String.CASE_INSENSITIVE_ORDER)
.orElse("NONE"));
}
}
/* output: 32 a you */
min Methods and max The return type of the method is Optional, This requires us to use orElse() To unpack .
5.7 Digital stream information
average(): Take the average of the stream elements .max()andmin(): Numerical flow operations do not require Comparator.sum(): Sum all flow elements .summaryStatistics(): Generate potentially useful data .( It's not what it takes. )
public class NumericStreamInfo {
public static void main(String[] args) {
System.out.println(rands().average().getAsDouble());
System.out.println(rands().max().getAsInt());
System.out.println(rands().min().getAsInt());
System.out.println(rands().sum());
System.out.println(rands().summaryStatistics());
}
}
/* output: 507.94 998 8 50794 IntSummaryStatistics{count=100, sum=50794, min=8, average=507.940000, max=998} */
5.8 matching
allMatch(predicate): If each element in the stream is provided to Predicate All back to true, The result returned to true. At the first false when , The calculation is stopped .anyMatch(predicate): If any element of the stream is provided to Predicate return true, The result returned to true. At the first true Stop counting .noneMatch(predicate): If each element of the stream is provided to Predicate All back to false when , The result returned to true. At the first true Stop execution of the calculation .
We have seen it before noneMatch() Use , The other two are similar . To eliminate redundancy , We created show(). First of all, we must know how to uniformly describe the operations of the three matchers , And then turn it into Matcher Interface .
interface Matcher extends BiPredicate<Stream<Integer>, Predicate<Integer>> {
}
public class Matching {
static void show(Matcher match, int val) {
System.out.println(
match.test(
IntStream.rangeClosed(1, 9)
.boxed()
.peek(n -> System.out.format("%d ", n)),
n -> n < val));
}
public static void main(String[] args) {
show(Stream::allMatch, 10);
show(Stream::allMatch, 4);
show(Stream::anyMatch, 2);
show(Stream::anyMatch, 0);
show(Stream::noneMatch, 5);
show(Stream::noneMatch, 0);
}
}
/* output: 1 2 3 4 5 6 7 8 9 true 1 2 3 4 false 1 true 1 2 3 4 5 6 7 8 9 false 1 false 1 2 3 4 5 6 7 8 9 true */
BiPredicate It's a binary predicate , It takes two parameters and returns true perhaps false. The first parameter is the stream we want to test , The second parameter is a predicate Predicate.Matcher Can match all Stream::Match Method , So you can put every Stream::Match Method reference passed to show In the method , Yes match.test() The call to is converted to a method reference Stream::Match Call to .
show() Parameters of val In the judgment test n < val The maximum value... Is specified in .show Method produces an integer 1-9 A stream composed of .peek() It is used to check the test step before testing the short circuit . It can be seen from the output that a short circuit has occurred each time .
边栏推荐
- \Processing method of ufeff
- 【实战】STM32 FreeRTOS移植系列教程5:FreeRTOS消息队列
- Topic34——31. Next spread
- R language uses as The character function converts date vector data to string (character) vector data
- stm32mp1 Cortex M4开发篇13:扩展板按键外部中断
- Unity中的地平面简介
- [actual combat] STM32 FreeRTOS migration series tutorial 7: FreeRTOS event flag group
- leetcode:19. Delete the penultimate node of the linked list
- R language uses as The date function converts a single character variable to date data and specifies the format of the data format conversion
- Vuforia引擎支持的版本
猜你喜欢

【实战】STM32 FreeRTOS移植系列教程1:FreeRTOS 二值信号量使用

一条命令开启监控之旅!

The skill of using ADB and the principle of USB communication

Full stack development

Unity中的地平面简介
![[practice] stm32mp157 development tutorial FreeRTOS system 3: FreeRTOS counting semaphore](/img/b1/e4b944877fecc079a772b81c55bfc8.jpg)
[practice] stm32mp157 development tutorial FreeRTOS system 3: FreeRTOS counting semaphore

为什么 C# 访问 null 字段会抛异常?

The spring recruitment is also terrible. Ali asked at the beginning of the interview: how to design a high concurrency system? I just split

2. the development of the meta universe
![[actual combat] STM32 FreeRTOS porting series Tutorial 4: FreeRTOS software timer](/img/16/ad38288689f629106a19a0b8defea2.jpg)
[actual combat] STM32 FreeRTOS porting series Tutorial 4: FreeRTOS software timer
随机推荐
111. solve the problem of prohibiting scripts from running on vs code. For more information, see error reporting
Appareils pris en charge par Arcore
Embedded software project process and project startup instructions (example)
Binary search (non recursive, no repeating elements)
并发底层原理:线程、资源共享、volatile 关键字
uni-app进阶之创建组件/原生渲染【day9】
R language through rprofile Site file, user-defined configuration of R language development environment startup parameters, shutdown parameters, user-defined specified cran local image source download
R language uses as The character function converts date vector data to string (character) vector data
音视频格式简介、编解码、音视频同步
The R language uses the sink function to export the string to the txt file in the specified directory. If no directory is specified, it will be output to the current working dir
The internal structure of MySQL and how an SQL statement is executed
optional类,便利函数,创建Optional,Optional对象操作以及Optional流
A command starts the monitoring journey!
Eureka的TimedSupervisorTask类(自动调节间隔的周期性任务)
How to connect the Internet - FTTH
Application configuration management, basic principle analysis
stm32mp1 Cortex M4开发篇10:扩展板数码管控制
leetcode:19. Delete the penultimate node of the linked list
Retrofit Extended reading
[JUC series] completionservice of executor framework