This article has been published exclusively by Backend Technology Picks.

Flows allow programmers to operate on collections at a level of abstraction.

Iterate from outside to inside

What are external and internal iterations?

In my opinion, outside and inside are relative to collection code.

If the iterated business is performed in the application code, it is called external iteration.

Instead, the iterated business is performed in collection code, called inner iteration (functional programming).

The language description can be a bit abstract, so let’s look at an example.

1. External iteration

The itrator method is called to generate a new Iterator object and control the entire iteration.

for (Student student:list){
    if(student.getAge()>18){ result++; }}Copy the code

As we all know, the underlying iterator used for is:

Iterator<Student> iterator = list.iterator();
while (iterator.hasNext()){
    Student student = iterator.next();
    if(student.getAge()>18){ result++; }}Copy the code

The iterative method above is external iteration.

Disadvantages of external iteration:
  1. It is difficult to abstract complex operations
  2. It’s essentially a serialization operation.

2. Internal iteration

Returns the response interface in the internal iteration: Stream

long count = list.stream().filter(student -> student.getAge() > 18).count();
Copy the code

The whole process is broken down into filtering and counting.

Note that the Stream object returned is not a new collection, but a recipe for creating it.

2.1 Lazy evaluation and early evaluation

A method that describes a Stream with a value like filter and does not end up producing a new collection is called lazy evaluation. Methods such as count that eventually produce values from Stream are called early evaluation.

To determine whether an operation is lazy or evaluated early, just look at its return value. If the return value is Stream, the evaluation is lazy; If the return value is another value or null, it is evaluated early. The ideal way to do these operations is to form a chain of lazy evaluations that eventually return the desired result with an early evaluation.

The whole process is similar to the builder pattern, with a series of operations followed by a call to the build method to return the desired object. Design mode fast learning (4) Builder mode

What’s good about this process? You can concatenate multiple operations on a collection class, but you only need to iterate once.

3. Common operations

3.1 Collect (toList()) early evaluation

The collect(toList()) method generates a list of values from the Stream and is an early evaluation operation.

List<String> collect = Stream.of("a"."b"."c").collect(Collectors.toList());
Copy the code

Stream.of(“a”, “b”, “c”) generates a Stream object from the List, and then collect(Collectors. ToList ()) generates a List object.

3.2 the map

A map can convert a value of one type to another.

List<String> streamMap = Stream.of("a"."b"."c").map(String -> String.toUpperCase()).collect(Collectors.toList());
Copy the code

Map (String -> string.toupperCase ()) returns a Stream object with uppercase letters of all letters. Collect (Collectors. ToList ()) returns a List.

3.3 Filter

To traverse and examine the elements, use filter

List<String> collect1 = Stream.of("a"."ab"."abc")
        .filter(value -> value.contains("b"))
        .collect(Collectors.toList());
Copy the code
3.4 flatMap

If you have an object with multiple sets and want to get the set of all numbers, you can use a flatMap

List<Integer> collect2 = Stream.of(asList(1, 2), asList(3, 4))
        .flatMap(Collection::stream)
        .collect(Collectors.toList());
Copy the code

Stream.of(asList(1, 2), asList(3, 4)) converts each collection to a Stream object, and.flatMap processes the new Stream object.

3.5 the Max and min

You can tell by the name, maximum and minimum.

Student student1 = list.stream()
        .min(Comparator.comparing(student -> student.getAge()))
        .get();
Copy the code

Java8 provides a static Comparator method that allows you to implement a convenient Comparator. Where Comparator.comparing(student -> student.getage () can be replaced by Comparator.comparing(student ::getAge) to be a purer lambda. Max in the same way.

3.6 the reduce

The reduce operation can generate a value from a set of values. The count, min, and Max methods used in the above example are actually reduce operations.

Integer reduce = Stream.of(1, 2, 3).reduce(0, (acc, element) -> acc + element);
System.out.println(reduce);

6
Copy the code

The above example uses reduce summation, where 0 represents the starting point, acc represents the accumulator, and holds the result of the current summation (each step adds elements from stream to ACC), and Element is the current element.

4. Operation integration

  1. The collect(toList()) method generates a list of values from the Stream
  2. A map can convert a value of one type to another.
  3. To traverse and examine the elements, use filter
  4. If you have an object with multiple sets and want to get the set of all numbers, you can use a flatMap
  5. The Max and min
  6. Reduce (not often used)

5. Chain operation actual combat

List<Student> students = new ArrayList<>();
students.add(new Student("Fant.J", 18)); students.add(new Student("Xiao Ming", 19)); students.add(new Student("Wang", 20)); students.add(new Student("Xiao li", 22)); List<Class> classList = new ArrayList<>(); classList.add(new Class(students,"1601"));
classList.add(new Class(students,"1602"));
Copy the code
static class Student{ private String name; private Integer age; getter and setter ... and construct .... } static class Class{ private List<Student> students; private String className; getter and setter ... and construct .... }Copy the code

Here are our data and relationships — class and student. Now I want to find students whose names start with small, using stream chain:

List<String> list= students.stream()
                            .filter(student -> student.getAge() > 18)
                            .map(Student::getName)
                            .collect(Collectors.toList());
Copy the code
[Xiao Ming, Xiao Wang, Xiao Li]Copy the code

This is a simple chain implementation of a Stream for students. What if I want to find objects older than 18 in many classes?

6. Improve in practice

Look for names older than 18 in many classes and return the collection.

Original code:

        List<String> nameList = new ArrayList<>();
        for (Class c:classList){
            for (Student student:c.getStudents()){
                if (student.getAge()>18){
                    String name = student.getName();
                    nameList.add(name);
                }
            }
        }

        System.out.println(nameList);
Copy the code

Chained stream code: if you had to write it, you would probably do classlist.stream ().foreach (aClass -> aclass.getStudents ().stream())…. To achieve it?

That’s how I started, but then I woke up and remembered that foreach was an early evaluation operation that returned void, so the beginning was doomed to be fruitless. Then I thought, isn’t flatMap used to handle streams that aren’t collections?

List<String> collect = classList.stream()
        .flatMap(aclass -> aclass.getStudents().stream())
        .filter(student -> student.getAge() > 18)
        .map(Student::getName)
        .collect(toList());
Copy the code

The original code has the following disadvantages compared to the stream chain call:

  1. The code is poorly readable and hides the real business logic
  2. Irrelevant variables need to be set to hold intermediate results
  3. Inefficient, each step evaluates early to generate a new set
  4. Difficult to parallelize

7. Higher-order functions and matters needing attention

A higher-order function is one that takes another function as an argument, or returns a function. A function is a higher-order function if its function contains or returns an interface.

Almost all functions in the Stream interface are higher-order functions. For example, Comparing takes a function as an argument and returns the Comparator interface.

Student student = list.stream().max(Comparator.comparing(Student::getAge)).get();

public interface Comparator<T> {}
Copy the code

The foreach method is also a higher-order function:

void forEach(Consumer<? super T> action);

public interface Consumer<T> {}
Copy the code

Passing a lambda expression to a higher-order function on a Stream is not recommended, except for higher-order functions that have some similar functionality, because most higher-order functions have the side effect of assigning values to variables. Local variables assign values to member variables, making it hard to detect.

Take ActionEvent as an example: