Author: Tangyuan

Personal blog: Javalover.cc

preface

Always friend friend before call, feeling have close suspicion, so behind still change a name to everybody

Because people come to see things, we call them officials (inspired by the Jin Ping Mei, one of the four famous novels spread among the people).

Officials hello, I am tangyuan, today to bring you is “Java8 in the Stream operation – entry”, I hope to help, thank you

The article is purely original, personal summary is inevitable mistakes, if so, please reply in the comment section or background private letter, thanks

Introduction to the

Streaming operations, also known as functional operations, are new to Java8

Streaming operations are primarily used for processing data (e.g., collections), just as generics are mostly used for collections.

Lambda expressions, functional interfaces, method references, etc., will be used in the following sections.

Let’s take a look at the table of contents

directory

  1. What is the flow

  2. Chestnuts, boss

  3. Flow operation steps

  4. The characteristics of the flow

  5. The difference between streaming and collection operations

The body of the

1. What is flow

A stream is an API that processes data in a declarative way

What is a declarative way?

Just declare, not implement, kind of abstract method (polymorphism)

2. Serve chestnuts, boss

Let’s take a look at what streaming is, and then introduce the following concepts for this. Okay

Requirements: Screen cats older than 1 (cat 1 ≈ human 5 years) and sort them in ascending order by age. Finally, extract names and store them separately in the list


public class BasicDemo {
    public static void main(String[] args) {
      // The following cat names are real, non-fictional
        List<Cat> list = Arrays.asList(new Cat(1."tangyuan"), new Cat(3."dangdang"), new Cat(2."milu"));
        // === old code before Java8 ===
        List<Cat> listTemp = new ArrayList<>();
        / / 1. Screening
        for(Cat cat: list){
            if(cat.getAge()>1){ listTemp.add(cat); }}/ / 2. Sort
        listTemp.sort(new Comparator<Cat>() {
            @Override
            public int compare(Cat o1, Cat o2) {
                // Increment sort
                returnInteger.compare(o1.getAge(), o2.getAge()); }});// 3
        List<String> listName = new ArrayList<>();
        for(Cat cat: listTemp){
            listName.add(cat.getName());
        }
        System.out.println(listName);
        
        // === new code Java8 after === =
        List<String> listNameNew = list.stream()
          			// Boolean test(T T) abstract method of Predicate
                .filter(cat -> cat.getAge() > 1)
								// Method references to lambda expressions
			          .sorted(Comparator.comparingInt(Cat::getAge))
          			// function interface Funtion R apply(T T) abstract method
                .map(cat-> cat.getName())
             	  // Collect the data and turn the stream into a collection List.collect(Collectors.toList()); System.out.println(listNameNew); }}class Cat{
    int age;
    String name;

    public Cat(int age, String name) {
        this.age = age;
        this.name = name;
    }
	/ / omit getter/setter
}

Copy the code

As you can see, the code is much cleaner with streaming (wow)

Q: Some officials might think that this is a bit like the combination of lambda expressions.

A: You’re right. It’s just like. There’s A big difference. Because combinatorial operations on lambda expressions are actually direct operations on collections;

We’ll skip over the difference between direct and streaming operations at the end of this article

Below we are based on this chestnut, respectively, to introduce the knowledge points involved

3. Flow operations

Let’s ignore the collection operations of the old version (more on the difference between streams and collections later) and start with the flow operations (after all, streams are the story of the day).

The operation of a stream is divided into three steps: create a stream, intermediate operation, and terminal operation

The process is as follows:

Here’s an important point to focus on:

An intermediate operation does not perform any processing before the terminal operation begins; it simply declares what to do;

You can think of this as an assembly line: let’s do a simplification here

  1. Purpose: To tell you first, we are going to process bottled water (create the flow first, tell you which data to process)
  2. For these bottles and water, build an assembly line: the clamps to hold the bottles, the pipes to hold the water, the claws to twist the LIDS, and the packers for packing (intermediate operation, declared operation to be performed).
  3. Finally, the start button is pressed, and the pipeline starts working (terminal operation, starting to process data according to intermediate operation)

Because each intermediate operation returns a Stream, they can be combined forever (did I eat something?). , but their combination order is not fixed, the flow will choose the appropriate combination order according to the system performance

We can print something to look at:

List<Cat> list = Arrays.asList(new Cat(1."tangyuan"), new Cat(3."dangdang"), new Cat(2."milu"));
List<String> listNameNew = list.stream()
  .filter(cat -> {
    System.out.println("filter: " + cat);
    return cat.getAge() > 1;
  })
  .map(cat-> {
    System.out.println("map:" + cat);
    return cat.getName();
  })
  .collect(Collectors.toList());
Copy the code

The output is as follows:

filter: Cat{age=1}
filter: Cat{age=3}
map:Cat{age=3}
filter: Cat{age=2}
map:Cat{age=2}
Copy the code

As you can see, the filter and map of the intermediate operation are combined and crossed, even though they are two separate operations (this technique is called loop merging).

This merge is largely dictated by streaming operations depending on the performance of the system

Now that we’re talking about loop merging, let’s say a little bit about short circuit techniques

A->B->C = A->C = A->C = A->C = A->C = A->C

A short circuit here refers to an intermediate operation that, for some reason (such as limit below), is partially executed but not fully executed

Let’s modify the above example (with an intermediate operation limit) :

List<Cat> list = Arrays.asList(new Cat(1."tangyuan"), new Cat(3."dangdang"), new Cat(2."milu"));
List<String> listNameNew = list.stream()
  .filter(cat -> {
    System.out.println("filter: " + cat);
    return cat.getAge() > 1;
  })
  .map(cat-> {
    System.out.println("map:" + cat);
    return cat.getName();
  })
  // Only this line is added
  .limit(1)
  .collect(Collectors.toList());
Copy the code

The output is as follows:

filter: Cat{age=1}
filter: Cat{age=3}
map:Cat{age=3}
Copy the code

As you can see, because limit(1) only needs one element, the filter will stop as long as it finds one that meets the condition (the following elements will be abandoned). This technique is called the short-circuit technique

This largely reflects the advantages brought by the combination order of intermediate operations: how much is needed, how much is processed, that is, as needed

4. Characteristics of flow

There are three characteristics:

  • Declarative: Concise, easy to read, with significantly fewer lines of code (except for companies that require lines per day)
  • Composable: more flexible, all kinds of combinations (as long as you want, as long as the stream has)
  • Parallelism: better performance (we don’t have to write multithreading, how nice)

5. Difference between stream operation and set operation:

Now let’s revisit the collection operations in the first example: filter -> Sort -> extract

List<Cat> listTemp = new ArrayList<>();
/ / 1. Screening
for(Cat cat: list){
  if(cat.getAge()>1){ listTemp.add(cat); }}/ / 2. Sort
listTemp.sort(new Comparator<Cat>() {
  @Override
  public int compare(Cat o1, Cat o2) {
    // Increment sort
    return Integer.compare(o1.getAge(), o2.getAge());
		/** * Q: return o1.getage () -o2.getage ()? * A: Because there is A data overflow risk with subtraction * if o1.getage () is 2 billion and O2.getage () is -200 million, the result will exceed the limit of int by more than 2.1 billion **/}});// 3
List<String> listName = new ArrayList<>();
for(Cat cat: listTemp){
  listName.add(cat.getName());
}
System.out.println(listName);
Copy the code

You can see two differences from streaming operations:

  1. The collection operation has a temporary listTemp variable.
  2. Collection operations process data all the time (whereas streaming operations do not process data until the last terminal operation), and filter -> sort -> extract names in order

Below we use a table to list the differences, should be straight point

Current operation Set operations
function Processing data primarily Store data primarily
Iterative way Internal iteration (iterates only once), only need to declare, do not need to implement, the flow has its own implementation) External iterations (which can always be iterations) require their own foreach
Process the data Data is not really processed until terminal operation (on-demand processing) Processing data all the time (all processing)

If you use real life examples, you can use the movie analogy

Streaming is like watching online, gathering is good for local viewing (download to local)

conclusion

  1. What is flow:
    • A stream is an API that processes data in a declarative way
    • Flow from supportData processing operationtheThe sourceThe generatedElements in the sequence
      • Source: The source of data, such as collections, files, etc. I’ll talk about the rest later)
      • Data processing operations: intermediate operations of a stream, such as filter and map
      • Sequence of elements: Result set returned by terminal operation of stream
  2. Flow operation flow:
    • Create a flow -> Intermediate Action -> Terminal action
    • Intermediate operations are declarations, do not actually process data, and are not performed until terminal operations begin
  3. Loop merge: Intermediate operations are freely combined (the flow can be combined in a sequence determined by the system itself)
  4. Short-circuit technique: If the intermediate operation has processed enough data, it will immediately stop processing data (for example, limit(1), it will stop processing when 1 data is processed)
  5. The difference between a stream operation and a collection operation:
    • Streams are processed as needed and collections are processed as a whole
    • Streams focus on data processing and collections focus on data storage
    • Flow is concise, set is not
    • Flow internal iteration (only iterate once, after which the flow will disappear), set external iteration (can always iterate)

Afterword.

And finally, thank you for watching. Thank you

Original is not easy, look forward to the three even yo