[method reference, Lambda expression, Stream stream]

[method reference, Lambda expression, Stream stream]

Today's goal

  • Thread state
  • Waiting and awakening
  • Lambda expression
  • Stream stream

Teaching objectives

  • Be able to name the six states of the thread
  • Be able to understand cases of waiting for wake-up
  • Be able to master the standard format and ellipsis format of Lambda expression
  • The stream can be obtained by collection, mapping or array
  • Be able to master common flow operations
  • The content of the stream can be collected into collections and arrays

Chapter 1 new features of JDK8

New JDK features:

Lambda expression

Default method [learned]

Stream API

Method reference

Base64

1.1 method reference

5.1.1 method Reference Overview

Method reference enables developers to directly reference existing methods, Java class construction methods or instance objects. Method reference and Lambda expression are used together to make the construction method of Java class look compact and concise without many complex template codes.

5.1.2 basic use of method reference

Method references use a pair of colons::.

Next, we define four methods in the Car class as examples to distinguish the references of four different methods in Java.

public static class Car {
    public static Car create( final Supplier< Car > supplier ) {
        return supplier.get();
    }              

    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }

    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }

    public void repair() {   
        System.out.println( "Repaired " + this.toString() );
    }
}

The type referenced by the first method is constructor reference. The syntax is Class::new, or a more general form: Class::new. Note: this constructor has no parameters.

final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );

The second type of method reference is static method reference, and the syntax is Class::static_method. Note: this method accepts a Car type parameter.

cars.forEach( Car::collide );

The third method refers to the member method of a class. The syntax is Class::method. Note that this method has no defined input parameters:

cars.forEach( Car::repair );

The fourth method refers to the member method of an instance object. The syntax is instance::method. Note: this method accepts a Car type parameter:

final Car police = Car.create( Car::new );
cars.forEach( police::follow );

5.1.3 code demonstration based on static method reference

public static void main(String args[]) {
    List names = new ArrayList();

    names.add("Daming");
    names.add("Er Ming");
    names.add("Xiao Ming");

    names.forEach(System.out::println);
}

In the above code, we will system The out:: println method is referenced as a static method.

The test results are:

Daming
 Er Ming
 Xiao Ming

Chapter 2, Lambda expression, research

2.1 overview of functional programming ideas

In mathematics, a function is a set of calculation schemes with input and output, that is, "what to do with". Relatively speaking, object-oriented overemphasizes that "things must be done in the form of objects", while functional thinking tries to ignore the complex object-oriented syntax - emphasizing what to do rather than what form to do.

What to do, not how to do it

Do we really want to create an anonymous inner class object? no We just have to create an object to do this. What we really want to do is pass the code in the run method body to the Thread class.

Pass a piece of code -- that's our real purpose. Creating objects is only a means that has to be taken due to the limitation of object-oriented syntax. So, is there a simpler way? If we return our focus from "how to do" to "what to do", we will find that as long as we can better achieve the goal, the process and form are not important.

2.2 Lambda optimization

When a thread needs to be started to complete a task, it is usually through Java Lang. runnable interface to define the task content, and use Java Lang. thread class to start the thread.

Traditionally, the code is as follows:

public class Demo01ThreadNameless {
	public static void main(String[] args) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("Multithreaded task execution!");
			}
		}).start();
	}
}

Based on the idea of "everything is an object", this approach is understandable: first, create an anonymous internal class object of Runnable interface to specify the task content, and then hand it over to a thread to start.

Code analysis:

For the anonymous inner class usage of Runnable, several points can be analyzed:

  • The Thread class requires the Runnable interface as a parameter, and the abstract run method is used to specify the core of the Thread task content;
  • In order to specify the method body of run, the implementation class of Runnable interface must be required;
  • In order to save the trouble of defining a RunnableImpl implementation class, anonymous inner classes have to be used;
  • The abstract run method must be overridden, so the method name, method parameters and method return value must be written again without error;
  • In fact, it seems that only the method body is the key.

Lambda expression is written in the following code:

With the new syntax of Java 8, the anonymous internal class writing method of the above Runnable interface can be equivalent through a simpler Lambda expression:

public class Demo02LambdaRunnable {
	public static void main(String[] args) {
		new Thread(() -> System.out.println("Multithreaded task execution!")).start(); // Start thread
	}
}

The execution effect of this code is exactly the same as that just now. It can be passed at the compilation level of 1.8 or higher. From the semantics of the code, we can see that we start a thread, and the content of the thread task is specified in a more concise form.

There is no longer the constraint of "having to create interface objects", and there is no longer the burden of "abstract method override", which is so simple!

2.3 format of lambda

Standard format:

Lambda omits the object-oriented rules and regulations, and the format consists of three parts:

  • Some parameters
  • An arrow
  • A piece of code

The standard format for Lambda expressions is:

(Parameter type parameter name) -> { Code statement }

Format Description:

  • The syntax in parentheses is consistent with the parameter list of traditional methods: leave blank if there is no parameter; Multiple parameters are separated by commas.
  • ->Is a newly introduced syntax format, which represents pointing action.
  • The syntax in braces is basically consistent with the requirements of traditional method body.

Comparison between anonymous inner class and lambda:

new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("Multithreaded task execution!");
			}
}).start();

After careful analysis of the code, the Runnable interface has only one definition of the run method:

  • public abstract void run();

That is, a plan for doing things has been formulated (in fact, it is a method):

  • No parameter: the scheme can be executed without any conditions.
  • No return value: the scheme does not produce any results.
  • Code block (method): the specific implementation steps of the scheme.

The same semantics is embodied in Lambda grammar, which should be simpler:

() -> System.out.println("Multithreaded task execution!")
  • The first pair of parentheses are the parameters of the run method (none), which means that no conditions are required;
  • An arrow in the middle represents passing the previous parameter to the following code;
  • The following output statement is the business logic code.

Parameters and return values:

The following example demonstrates Java util. Usage scenario code of comparator < T > interface, in which the abstract method is defined as:

  • public abstract int compare(T o1, T o2);

When you need to sort an array of objects, arrays The sort method requires a Comparator interface instance to specify the sorting rules. Suppose there is a Person class with two member variables: String name and int age:

public class Person { 
    private String name;
    private int age;
    
    // Omit constructor, toString method and Getter Setter 
}

Traditional writing

If the traditional code is used to sort the Person [] array, it is written as follows:

public class Demo06Comparator {
    public static void main(String[] args) {
      	// An unordered array of objects
        Person[] array = { new Person("Gulinaza", 19),        	new Person("Delireba", 18),       		new Person("Marzaha", 20) };

      	// Anonymous Inner Class 
        Comparator<Person> comp = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        };
        Arrays.sort(array, comp); // The second parameter is the collation, that is, the Comparator interface instance

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

This approach seems to be "taken for granted" in the idea of object-oriented. The instance of Comparator interface (using anonymous inner class) represents the sorting rule of "from small to large by age".

code analysis

Let's find out what the above code really does.

  • For sorting, arrays Sort method needs sorting rules, that is, the instance of Comparator interface, and the abstract method compare is the key;
  • In order to specify the method body of compare, you have to need the implementation class of the Comparator interface;
  • In order to save the trouble of defining a ComparatorImpl implementation class, anonymous inner classes have to be used;
  • The abstract compare method must be overridden, so the method name, method parameters and method return value must be written again and cannot be written wrong;
  • In fact, only parameters and method bodies are critical.

Lambda writing

public class Demo07ComparatorLambda {
    public static void main(String[] args) {
        Person[] array = {
          	new Person("Gulinaza", 19),
          	new Person("Delireba", 18),
          	new Person("Marzaha", 20) };

        Arrays.sort(array, (Person a, Person b) -> {
          	return a.getAge() - b.getAge();
        });

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

Omit format:

Omission rule

On the basis of Lambda standard format, the rules for using ellipsis are as follows:

  1. The types of parameters in parentheses can be omitted;
  2. If there is only one parameter in the parenthesis, the parenthesis can be omitted;
  3. If there is only one statement in braces, you can omit the braces, return keyword and statement semicolon regardless of whether there is a return value or not.

Note: after mastering these omission rules, please review the multithreading cases at the beginning of this chapter accordingly.

It can be deduced and omitted

Lambda emphasizes "what to do" rather than "how to do", so any information that can be deduced from the context can be omitted. For example, the above example can also use lambda's ellipsis:

Runnable Interface simplification:
1. () -> System.out.println("Multithreaded task execution!")
Comparator Interface simplification:
2. Arrays.sort(array, (a, b) -> a.getAge() - b.getAge());

2.4 prerequisites for lambda

Lambda's syntax is very concise and completely free from the constraints of object-oriented complexity. However, there are several problems that need special attention when using:

  1. Lambda must have an interface, and there must be only one abstract method in the interface.
    Whether it is the built-in Runnable, Comparator interface or user-defined interface of JDK, Lambda can be used only when the abstract methods in the interface exist and are unique.
  2. Using Lambda must have context inference.
    That is, the parameter or local variable type of the method must be the interface type corresponding to Lambda, and Lambda can be used as an instance of the interface.

Note: an interface with only one abstract method is called "functional interface".

Chapter III Stream

In Java 8, thanks to the functional programming brought by Lambda, a new Stream concept is introduced to solve the existing disadvantages of the existing collection class library.

3.1 introduction

Multi step traversal code of traditional collection

Almost all collections (such as Collection interface or Map interface) support direct or indirect traversal operations. When we need to operate on the elements in the Collection, in addition to the necessary addition, deletion and acquisition, the most typical is Collection traversal. For example:

public class Demo01ForEach {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("zhang wuji");
        list.add("Zhou Zhiruo");
        list.add("Zhao Min");
        list.add("Zhang Qiang");
        list.add("Zhang Sanfeng");
        for (String name : list) {
          	System.out.println(name);
        }
    }  
}

This is a very simple set traversal operation: print out each string in the set.

Disadvantages of loop traversal

Lambda of Java 8 allows us to focus more on What rather than How. This has been compared with internal classes. Now, let's take a closer look at the above example code, and we can find that:

  • The syntax of the for loop is "how"
  • The loop body of the for loop is "what to do"

Why use loops? Because of traversal. But is loop the only way to traverse? Traversal refers to the processing of each element one by one, rather than a cycle from the first to the last. The former is the purpose and the latter is the way.

Imagine if you want to filter the elements in the collection:

  1. Filter set A into subset B according to condition 1;
  2. Then it is filtered into subset C according to condition 2.

What can we do? Before Java 8, the practice may be as follows:

public class Demo02NormalFilter {
  	public static void main(String[] args) {
      	List<String> list = new ArrayList<>();
        list.add("zhang wuji");
        list.add("Zhou Zhiruo");
        list.add("Zhao Min");
        list.add("Zhang Qiang");
        list.add("Zhang Sanfeng");

        List<String> zhangList = new ArrayList<>();
        for (String name : list) {
            if (name.startsWith("Zhang")) {
              	zhangList.add(name);
            }
        }

        List<String> shortList = new ArrayList<>();
        for (String name : zhangList) {
            if (name.length() == 3) {
              	shortList.add(name);
            }
        }

        for (String name : shortList) {
          	System.out.println(name);
        }
    }
}

This code contains three loops, each with different functions:

  1. First, screen all people surnamed Zhang;
  2. Then screen people whose names have three words;
  3. Finally, print out the results.

Whenever we need to operate on the elements in the collection, we always need to cycle, cycle and recycle. Is this taken for granted** No** Circulation is a way of doing things, not an end. On the other hand, using a linear loop means that it can only be traversed once. If you want to iterate again, you can only start from scratch with another loop.

How can Lambda's derivative Stream bring us more elegant writing?

Better writing of Stream

Let's take a look at what elegance is with the Stream API of Java 8:

public class Demo03StreamFilter {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("zhang wuji");
        list.add("Zhou Zhiruo");
        list.add("Zhao Min");
        list.add("Zhang Qiang");
        list.add("Zhang Sanfeng");

        list.stream()
          	.filter(s -> s.startsWith("Zhang"))
            .filter(s -> s.length() == 3)
            .forEach(System.out::println);
    }
}

Directly reading the literal meaning of the code can perfectly display the semantics of irrelevant logical methods: obtain stream, filter surname Zhang, filter length 3, and print one by one. The code does not reflect the use of linear loops or any other algorithm for traversal. What we really want to do is better reflected in the code.

3.2 overview of streaming ideas

Note: Please temporarily forget the inherent impression of traditional IO streams!

On the whole, the flow idea is similar to the "production line" in the factory workshop.

When multiple elements need to be operated (especially multi-step operation), considering the performance and convenience, we should first spell out a "model" step scheme, and then execute it according to the scheme.

This figure shows multi-step operations such as filtering, mapping, skipping and counting. This is a processing scheme for collection elements, and the scheme is a "function model". Each box in the figure is a "flow", which can be converted from one flow model to another by calling the specified method. The number 3 on the far right is the final result.

The filter, map and skip here are all operating on the function model, and the collection elements are not really processed. Only when the termination method count is executed, the whole model will execute the operation according to the specified policy. This is due to Lambda's delayed execution feature.

Note: "Stream stream" is actually a functional model of collection elements. It is neither a collection nor a data structure. It does not store any elements (or their address values).

3.3 access flow method

java. util. stream. Stream < T > is the most commonly used stream interface newly added to Java 8. (this is not a functional interface.)

Obtaining a stream is very simple. There are several common methods:

  • All Collection collections can obtain streams through the stream default method;
  • The static method of the Stream interface can obtain the Stream corresponding to the array.

Method 1: get the stream according to the Collection

First, Java util. The default method stream is added to the collection interface to obtain the stream, so all its implementation classes can obtain the stream.

import java.util.*;
import java.util.stream.Stream;

public class Demo04GetStream {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        // ...
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        // ...
        Stream<String> stream2 = set.stream();

        Vector<String> vector = new Vector<>();
        // ...
        Stream<String> stream3 = vector.stream();
    }
}

Method 2: get the flow according to the Map

java.util.Map interface is not a sub interface of Collection, and its K-V data structure does not conform to the single characteristics of flow elements. Therefore, to obtain the corresponding flow, you need to divide it into key, value or entry:

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class Demo05GetStream {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        // ...
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valueStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
    }
}

Method 3: get the stream according to the array

If you are using an array instead of a collection or mapping, since it is impossible to add a default method to the array object, the Stream interface provides a static method of, which is very simple to use:

import java.util.stream.Stream;

public class Demo06GetStream {
    public static void main(String[] args) {
        String[] array = { "zhang wuji", "Zhang Cuishan", "Zhang Sanfeng", "Zhang Yiyuan" };
        Stream<String> stream = Stream.of(array);
    }
}

Note: the parameter of the of method is actually a variable parameter, so arrays are supported.

3.4 common methods

The operation of flow model is very rich. Here are some common API s. These methods can be divided into two types:

  • Termination method: the return value type is no longer the method of the Stream interface itself, so chained calls like StringBuilder are no longer supported. In this section, the termination methods include count and forEach methods.
  • Non terminating method: the return value type is still the method of the Stream interface itself, so chain call is supported. (except the terminating method, all other methods are non terminating methods.)

Function splicing and termination method

Among the various methods described above, those whose return value is still the Stream interface are function splicing methods, which support chain calls; The return value is no longer the end method of the Stream interface, and chain calls are no longer supported. As shown in the following table:

Method nameMethod functionMethod typeWhether chain call is supported
countNumber of StatisticsEndno
forEachProcess one by oneEndno
filterfilterFunction splicingyes
limitTake the first fewFunction splicingyes
skipSkip the first fewFunction splicingyes
mapmappingFunction splicingyes
concatcombinationFunction splicingyes

Note: for more methods beyond this section, please refer to the API documentation.

forEach: process one by one

Although the method name is forEach, unlike the nickname "for each" in the for loop, this method does not guarantee that the consumption actions of elements one by one are executed orderly in the flow.

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

This method receives a Consumer interface function and will give each stream element to this function for processing. For example:

import java.util.stream.Stream;

public class Demo12StreamForEach {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        stream.forEach(s->System.out.println(s));
    }
}

Count: count the number

Just like the size method in the old Collection, the flow provides the count method to count the number of elements:

long count();

This method returns a long value representing the number of elements (it is no longer an int value like the old collection). Basic usage:

public class Demo09StreamCount {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        Stream<String> result = original.filter(s -> s.startsWith("Zhang"));
        System.out.println(result.count()); // 2
    }
}

Filter: filter

You can convert a stream into another subset stream through the filter method. Method statement:

Stream<T> filter(Predicate<? super T> predicate);

The interface receives a Predicate functional interface parameter (which can be a Lambda or method reference) as a filter condition.

Basic use

The basic code used by the filter method in the Stream stream is as follows:

public class Demo07StreamFilter {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        Stream<String> result = original.filter(s -> s.startsWith("Zhang"));
    }
}

Here, the filter condition is specified through Lambda expression: must be Zhang.

limit: access the first few

The limit method can intercept convection, and only the first n can be used. Method signature:

Stream<T> limit(long maxSize);

The parameter is a long type. If the current length of the collection is greater than the parameter, it will be intercepted; Otherwise, do not operate. Basic usage:

import java.util.stream.Stream;

public class Demo10StreamLimit {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        Stream<String> result = original.limit(2);
        System.out.println(result.count()); // 2
    }
}

Skip: skip the first few

If you want to skip the first few elements, you can use the skip method to get a new stream after interception:

Stream<T> skip(long n);

If the current length of the stream is greater than N, skip the first n; Otherwise, you will get an empty stream with a length of 0. Basic usage:

import java.util.stream.Stream;

public class Demo11StreamSkip {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("zhang wuji", "Zhang Sanfeng", "Zhou Zhiruo");
        Stream<String> result = original.skip(2);
        System.out.println(result.count()); // 1
    }
}

map: mapping

If you need to map elements in a stream to another stream, you can use the map method. Method signature:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

This interface requires a Function type interface parameter, which can convert T-type data in the current stream into another R-type stream.

Basic use

The basic code used by the map method in the Stream stream is as follows:

import java.util.stream.Stream;

public class Demo08StreamMap {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("10", "12", "18");
        Stream<Integer> result = original.map(s->Integer.parseInt(s));
    }
}

In this code, the parameters of the map method convert the string type into the int type through the method reference (and are automatically boxed into an Integer class object).

concat: combination

If there are two streams and you want to merge them into one Stream, you can use the static method concat of the Stream interface:

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)

Note: This is a static method, similar to Java The concat method in lang. string is different.

The basic usage code of this method is as follows:

import java.util.stream.Stream;

public class Demo12StreamConcat {
    public static void main(String[] args) {
        Stream<String> streamA = Stream.of("zhang wuji");
        Stream<String> streamB = Stream.of("Zhang Cuishan");
        Stream<String> result = Stream.concat(streamA, streamB);
    }
}

3.5 Stream comprehensive case

Now there are two ArrayList collections to store the names of multiple members in the team. The traditional for loop (or enhanced for loop) is required to perform the following steps in turn:

  1. The first team only needs the name of the member whose name is three words;
  2. After the first team screening, only the first three people;
  3. The second team only needs the names of members surnamed Zhang;
  4. After the second team is screened, do not use the first two people;
  5. Merge the two teams into one team;
  6. Create a Person object based on the name;
  7. Print the Person object information of the whole team.

The codes of the two teams (sets) are as follows:

public class DemoArrayListNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("Delireba");
        one.add("Song Yuanqiao");
        one.add("Su Xinghe");
        one.add("Laozi");
        one.add("Zhuangzi");
        one.add("grandson");
        one.add("master hongqi");

        List<String> two = new ArrayList<>();
        two.add("Gulinaza");
        two.add("zhang wuji");
        two.add("Zhang Sanfeng");
        two.add("Zhao Liying");
        two.add("Zhang Ergou");
        two.add("Zhang Tianai");
        two.add("Zhang San");
		// ....
    }
}

The code of the Person class is:

public class Person {
    
    private String name;

    public Person() {}

    public Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "'}";
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Traditional way

Using the for loop, example code:

public class DemoArrayListNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        // ...

        List<String> two = new ArrayList<>();
        // ...

        // The first team only needs the name of the member whose name is three words;
        List<String> oneA = new ArrayList<>();
        for (String name : one) {
            if (name.length() == 3) {
                oneA.add(name);
            }
        }

        // After the first team screening, only the first three people;
        List<String> oneB = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            oneB.add(oneA.get(i));
        }

        // The second team only needs the names of members surnamed Zhang;
        List<String> twoA = new ArrayList<>();
        for (String name : two) {
            if (name.startsWith("Zhang")) {
                twoA.add(name);
            }
        }

        // After the second team is screened, do not use the first two people;
        List<String> twoB = new ArrayList<>();
        for (int i = 2; i < twoA.size(); i++) {
            twoB.add(twoA.get(i));
        }

        // Merge the two teams into one team;
        List<String> totalNames = new ArrayList<>();
        totalNames.addAll(oneB);
        totalNames.addAll(twoB);

        // Create a Person object based on the name;
        List<Person> totalPersonList = new ArrayList<>();
        for (String name : totalNames) {
            totalPersonList.add(new Person(name));
        }

        // Print the Person object information of the whole team.
        for (Person person : totalPersonList) {
            System.out.println(person);
        }
    }
}

The operation result is:

Person{name='Song Yuanqiao'}
Person{name='Su Xinghe'}
Person{name='master hongqi'}
Person{name='Zhang Ergou'}
Person{name='Zhang Tianai'}
Person{name='Zhang San'}

Stream mode

The equivalent Stream stream processing code is:

public class DemoStreamNames {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        // ...

        List<String> two = new ArrayList<>();
        // ...

        // The first team only needs the name of the member whose name is three words;
        // After the first team is screened, only the first three people;
        Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);

        // The second team only needs the names of members surnamed Zhang;
        // After the second team is screened, do not use the first two people;
        Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("Zhang")).skip(2);

        // Merge the two teams into one team;
        // Create a Person object based on the name;
        // Print the Person object information of the whole team.
        Stream.concat(streamOne, streamTwo).map(s-> new Person(s)).forEach(s->System.out.println(s));
    }
}

The operation effect is exactly the same:

Person{name='Song Yuanqiao'}
Person{name='Su Xinghe'}
Person{name='master hongqi'}
Person{name='Zhang Ergou'}
Person{name='Zhang Tianai'}
Person{name='Zhang San'}

3.6 collect Stream results

After the convection operation is completed, how to collect the results, such as obtaining the corresponding set, array, etc?

Collect into collection

The Stream stream provides the collect method, and its parameters need a Java util. Stream. Collector < T, a, R > interface object to specify which collection to collect. Fortunately, Java util. Stream. The collectors class provides methods that can be used as instances of the collector interface:

  • public static <T> Collector<T, ?, List < T > > tolist(): convert to list set.
  • public static <T> Collector<T, ?, Set < T > > toset(): convert to set.

Here are the basic usage codes of these two methods:

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Demo15StreamCollect {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
        List<String> list = stream.collect(Collectors.toList());
        Set<String> set = stream.collect(Collectors.toSet());
    }
}

Collect into array

Stream provides toArray method to put the result into an array. Due to generic erasure, the return value type is Object []:

Object[] toArray();

Its usage scenarios are as follows:

import java.util.stream.Stream;

public class Demo16StreamArray {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("10", "20", "30", "40", "50");
        Object[] objArray = stream.toArray();
    }
}

Chapter 4 File class

4.1 General

java.io.File class is an abstract representation of file and directory pathnames. It is mainly used for the creation, search and deletion of files and directories.

4.2 construction method

  • public File(String pathname): create a new File instance by converting the given pathname string to an abstract pathname.

  • public File(String parent, String child): creates a new File instance from the parent pathname string and the child pathname string.

  • public File(File parent, String child): creates a new File instance from the parent abstract pathname and child pathname strings.

  • For example, the code is as follows:

// File pathname
String pathname = "D:\\aaa.txt";
File file1 = new File(pathname); 

// File pathname
String pathname2 = "D:\\aaa\\bbb.txt";
File file2 = new File(pathname2); 

// Pass parent and child path strings
 String parent = "d:\\aaa";
 String child = "bbb.txt";
 File file3 = new File(parent, child);

// String through parent File object and child path
File parentDir = new File("d:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child);

Tips:

  1. A File object represents a File or directory that actually exists in the hard disk.
  2. Whether a File or directory exists in this path does not affect the creation of the File object.

4.3 common methods

Method of obtaining function

  • public String getAbsolutePath(): returns the absolute pathname string of this File.

  • public String getPath(): converts this File to a pathname string.

  • public String getName(): returns the name of the File or directory represented by this File.

  • public long length(): returns the length of the File represented by this File.

    Method demonstration, the code is as follows:

    public class FileGet {
        public static void main(String[] args) {
            File f = new File("d:/aaa/bbb.java");     
            System.out.println("File absolute path:"+f.getAbsolutePath());
            System.out.println("File construction path:"+f.getPath());
            System.out.println("File name:"+f.getName());
            System.out.println("file length:"+f.length()+"byte");
    
            File f2 = new File("d:/aaa");     
            System.out.println("Directory absolute path:"+f2.getAbsolutePath());
            System.out.println("Directory construction path:"+f2.getPath());
            System.out.println("Directory name:"+f2.getName());
            System.out.println("Directory length:"+f2.length());
        }
    }
    Output results:
    File absolute path:d:\aaa\bbb.java
     File construction path:d:\aaa\bbb.java
     File name:bbb.java
     file length:636 byte
    
    Directory absolute path:d:\aaa
     Directory construction path:d:\aaa
     Directory name:aaa
     Directory length:4096
    

Description in API: length() indicates the length of the File. However, if the File object represents a directory, the return value is not specified.

Absolute path and relative path

  • Absolute path: the path starting from the drive letter. This is a complete path.
  • Relative path: relative to the path of the project directory, this is a convenient path, which is often used in development.
public class FilePath {
    public static void main(String[] args) {
      	// BBB under disk D Java file
        File f = new File("D:\\bbb.java");
        System.out.println(f.getAbsolutePath());
      	
		// BBB under item Java file
        File f2 = new File("bbb.java");
        System.out.println(f2.getAbsolutePath());
    }
}
Output results:
D:\bbb.java
D:\idea_project_test4\bbb.java

Method of judging function

  • public boolean exists(): whether the File or directory represented by this File actually exists.
  • public boolean isDirectory(): whether this File indicates a directory.
  • public boolean isFile(): this File indicates whether it is a File.

Method demonstration, the code is as follows:

public class FileIs {
    public static void main(String[] args) {
        File f = new File("d:\\aaa\\bbb.java");
        File f2 = new File("d:\\aaa");
      	// Judge whether it exists
        System.out.println("d:\\aaa\\bbb.java Does it exist:"+f.exists());
        System.out.println("d:\\aaa Does it exist:"+f2.exists());
      	// Determine whether it is a file or directory
        System.out.println("d:\\aaa file?:"+f2.isFile());
        System.out.println("d:\\aaa catalogue?:"+f2.isDirectory());
    }
}
Output results:
d:\aaa\bbb.java Does it exist:true
d:\aaa Does it exist:true
d:\aaa file?:false
d:\aaa catalogue?:true

Method for creating and deleting functions

  • public boolean createNewFile(): creates a new empty file if and only if the file with this name does not exist yet.
  • public boolean delete(): deletes the File or directory represented by this File.
  • public boolean mkdir(): create the directory represented by this File.
  • public boolean mkdirs(): create the directory represented by this File, including any required but nonexistent parent directory.

Method demonstration, the code is as follows:

public class FileCreateDelete {
    public static void main(String[] args) throws IOException {
        // File creation
        File f = new File("aaa.txt");
        System.out.println("Does it exist:"+f.exists()); // false
        System.out.println("Create:"+f.createNewFile()); // true
        System.out.println("Does it exist:"+f.exists()); // true
		
     	// Directory creation
      	File f2= new File("newDir");	
        System.out.println("Does it exist:"+f2.exists());// false
        System.out.println("Create:"+f2.mkdir());	// true
        System.out.println("Does it exist:"+f2.exists());// true

		// Create multi-level directory
      	File f3= new File("newDira\\newDirb");
        System.out.println(f3.mkdir());// false
        File f4= new File("newDira\\newDirb");
        System.out.println(f4.mkdirs());// true
      
      	// Deletion of files
       	System.out.println(f.delete());// true
      
      	// Directory deletion
        System.out.println(f2.delete());// true
        System.out.println(f4.delete());// false
    }
}

API Description: delete method. If this File represents a directory, the directory must be empty to delete.

4.4 directory traversal

  • public String[] list(): returns a String array representing all sub files or directories in the File directory.

  • public File[] listFiles(): returns a File array representing all sub files or directories in the File directory.

public class FileFor {
    public static void main(String[] args) {
        File dir = new File("d:\\java_code");
      
      	//Get the name of the file and folder in the current directory.
		String[] names = dir.list();
		for(String name : names){
			System.out.println(name);
		}
        //Get the files and folder objects in the current directory. As long as you get the file objects, you can get more information
        File[] files = dir.listFiles();
        for (File file : files) {
            System.out.println(file);
        }
    }
}

Tips:

The File object calling the listFiles method must represent the actual directory, otherwise it returns null and cannot be traversed.

Chapter 5 recursion

5.1 general

  • Recursion: refers to the phenomenon of calling itself within the current method.
public static void a(){
    a();
}

5.2 recursive sum

Calculate the sum of 1 ~ n

Analysis: the cumulative sum of num = the cumulative sum of num + (num-1), so the cumulative sum operation can be defined as a method and called recursively.

Implementation code:

public class DiGuiDemo {
	public static void main(String[] args) {
		//Calculate the sum of 1~num, using recursion
		int num = 5;
      	// Call the summation method
		int sum = getSum(num);
      	// Output results
		System.out.println(sum);
		
	}
  	/*
  	  It is realized by recursive algorithm
  	  Parameter list: int 
  	  Return value type: int 
  	*/
	public static int getSum(int num) {
      	/* 
      	   num When it is 1, the method returns 1,
      	   Equivalent to the exit of the method, num is always 1
      	*/
		if(num == 1){
			return 1;
		}
      	/*
          num When it is not 1, the method returns the cumulative sum of num +(num-1)
          Call getSum method recursively
        */
		return num + getSum(num-1);
	}
}

Code execution diagram

[the external chain picture transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-djqe1ihv-1641466212751) (assets / day08_01_9% 80% 92% E5% BD% 92% E7% B4% AF% E5% 92% 8C. JPG)]

Tip: recursion must be conditionally limited to ensure that recursion can stop without too many times, otherwise stack memory overflow will occur.

5.3 recursive factorization

  • Factorial: the product of all positive integers less than or equal to the number.
n Factorial of: n! = n * (n-1) *...* 3 * 2 * 1 

Analysis: This is similar to summation, but it is replaced by multiplication. Students can practice by themselves. Note that the factorial value conforms to the range of int type.

It is inferred that: n! = n * (n-1)!

Code implementation:

public class DiGuiDemo {
  	//Calculate the factorial of n, using recursion
    public static void main(String[] args) {
        int n = 3;
      	// Call the factorial method
        int value = getValue(n);
      	// Output results
        System.out.println("Factorial is:"+ value);
    }
	/*
  	  It is realized by recursive algorithm
  	  Parameter list: int 
  	  Return value type: int 
  	*/
    public static int getValue(int n) {
      	// The factorial of 1 is 1
        if (n == 1) {
            return 1;
        }
      	/*
      	  n When it is not 1, the method returns n= n*(n-1)!
          Call getValue method recursively
      	*/
        return n * getValue(n - 1);
    }
}

5.4 file search

Search for in the D:\aaa directory java file.

analysis:

  1. Directory search, can not determine how many levels of directories, so use recursion to traverse all directories.
  2. When traversing the directory, obtain the sub files and judge whether they meet the conditions through the file name.

Code implementation:

public class DiGuiDemo3 {
    public static void main(String[] args) {
        // Create File object
        File dir  = new File("D:\\aaa");
      	// Call the print directory method
        printDir(dir);
    }

    public static void printDir(File dir) {
      	// Get sub files and directories
        File[] files = dir.listFiles();
      	
      	// Cyclic printing
        for (File file : files) {
            if (file.isFile()) {
              	// Yes, determine the file name and output the absolute path of the file
                if (file.getName().endsWith(".java")) {
                    System.out.println("file name:" + file.getAbsolutePath());
                }
            } else {
                // It is a directory. Continue to traverse to form recursion
                printDir(file);
            }
        }
    }
}

Chapter VI IO overview

6.1 what is IO

You must have experienced such a scene in your life. When you edit a text file and forget ctrl+s, the file may be edited in vain. When you insert a U SB flash disk into your computer, you can copy a video to your computer hard disk. So what devices are the data on? Keyboard, memory, hard disk, external devices, etc.

We can regard this data transmission as a kind of data flow. According to the flow direction and based on memory, it is divided into input input and output, that is, the flow to memory is the input flow and the output flow out of memory.

I/O operation in Java mainly refers to the use of Java Input and output the contents under the IO package. Input is also called read data, and output is also called write data.

6.2 classification of IO

According to the flow direction of data, it is divided into input flow and output flow.

  • Input stream: a stream that reads data from other devices into memory.
  • Output stream: a stream that writes data out of memory to other devices.

The types of pattern data are divided into byte stream and character stream.

  • Byte stream: a stream that reads and writes data in bytes.
  • Character stream: a stream that reads and writes data in character units.

6.3 flow direction illustration of IO

[external chain picture transfer failed. The source station may have anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-1Iazz10Y-1641466212751)(…/…/%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA%E8%AF%BE%E6%94%B9/day01-day15%E8%AE%B2%E4%B9%89-MD/day10-File%E7%B1%BB%E3%80%81%E9%80%92%E5%BD%92%E3%80%81%E5%AD%97%E8%8A%82%E6%B5%81%E3%80%81%E5%AD%97%E7%AC%A6%E6%B5%81/img/1_io.jpg)]

6.4 top level parent classes

Input streamOutput stream
Byte streamByte input stream
InputStream
Byte output stream
OutputStream
Character streamCharacter input stream
Reader
Character output stream
Writer

Chapter 7 byte stream

7.1 everything is a byte

All file data (text, pictures, videos, etc.) are stored in binary numbers, bytes by bytes, and the same is true during transmission. Therefore, byte stream can transfer any file data. When operating a stream, we should always make it clear that no matter what kind of stream object is used, the underlying transmission is always binary data.

7.2 byte output stream [OutputStream]

java.io.OutputStream abstract class is a superclass representing all classes of byte output stream, which writes the specified byte information to the destination. It defines the basic common function method of byte output stream.

  • public void close(): close this output stream and release any system resources associated with this stream.
  • public void flush(): flushes this output stream and forces any buffered output bytes to be written out.
  • public void write(byte[] b): writes b.length bytes from the specified byte array to this output stream.
  • public void write(byte[] b, int off, int len): writes len bytes from the specified byte array and outputs them to this output stream starting from offset off.
  • public abstract void write(int b): outputs the specified bytes to the stream.

Tips:

close method, which must be called to release system resources when the operation of the stream is completed.

7.3 FileOutputStream class

OutputStream has many subclasses. Let's start with the simplest subclass.

java. io. The fileoutputstream class is a file output stream used to write data out to a file.

Construction method

  • public FileOutputStream(File file): creates a File output stream to write to the File represented by the specified File object.
  • public FileOutputStream(String name): creates a file output stream and writes it to the file with the specified name.

When you create a stream object, you must pass in a file path. Under this path, if there is no such file, it will be created. If there is this file, the data of this file will be cleared.

  • For example, the code is as follows:
public class FileOutputStreamConstructor throws IOException {
    public static void main(String[] args) {
   	 	// Create a stream object using a File object
        File file = new File("a.txt");
        FileOutputStream fos = new FileOutputStream(file);
      
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("b.txt");
    }
}

Write byte data

  1. Write byte: write(int b) method can write one byte of data at a time. The code usage demonstration:
public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// Write data
      	fos.write(97); // Write the first byte
      	fos.write(98); // Write the second byte
      	fos.write(99); // Write the third byte
      	// close resource
        fos.close();
    }
}
Output results:
abc

Tips:

  1. Although the parameter is four bytes of int type, only one byte of information will be reserved.
  2. After the stream operation is completed, you must release system resources and call the close method. Remember.
  1. Write out byte array: write(byte[] b). You can write out the data in the array each time. The code usage demonstration:
public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// Convert string to byte array
      	byte[] b = "Dark horse programmer".getBytes();
      	// Write out byte array data
      	fos.write(b);
      	// close resource
        fos.close();
    }
}
Output results:
Dark horse programmer
  1. Write out the byte array of the specified length: write(byte[] b, int off, int len). Each write starts from the off index, len bytes. Code usage demonstration:
public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt");     
      	// Convert string to byte array
      	byte[] b = "abcde".getBytes();
		// Write 2 bytes starting from index 2. Index 2 is c, two bytes, that is, cd.
        fos.write(b,2,2);
      	// close resource
        fos.close();
    }
}
Output results:
cd

Data append and continue

After the above demonstration, each time the program runs and creates an output stream object, the data in the target file will be cleared. How to keep the data in the target file and continue to add new data?

  • Public fileoutputstream (File, Boolean append): creates a File output stream to write to the File represented by the specified File object.
  • public FileOutputStream(String name, boolean append): creates a file output stream and writes it to the file with the specified name.

For these two construction methods, a boolean value needs to be passed in the parameters. true means to append data, and false means to empty the original data. The output stream object created in this way can be used to specify whether to append and continue. The code uses the following demonstration:

public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt",true);     
      	// Convert string to byte array
      	byte[] b = "abcde".getBytes();
		// Write 2 bytes starting from index 2. Index 2 is c, two bytes, that is, cd.
        fos.write(b);
      	// close resource
        fos.close();
    }
}
Before file operation: cd
 After file operation: cdabcde

Write line breaks

On Windows systems, the newline symbol is \ r\n. hold

To specify whether to append and continue. The code usage demonstration:

public class FOSWrite {
    public static void main(String[] args) throws IOException {
        // Create a stream object with a file name
        FileOutputStream fos = new FileOutputStream("fos.txt");  
      	// Define byte array
      	byte[] words = {97,98,99,100,101};
      	// Traversal array
        for (int i = 0; i < words.length; i++) {
          	// Write a byte
            fos.write(words[i]);
          	// Write a newline and convert the newline symbol into an array
            fos.write("\r\n".getBytes());
        }
      	// close resource
        fos.close();
    }
}

Output results:
a
b
c
d
e
  • Carriage returns \ r and line breaks \ n:
    • Carriage return: return to the beginning of a line.
    • newline: next line.
  • Line breaks in the system:
    • In Windows system, the end of each line is enter + line feed, that is \ r\n;
    • In Unix systems, there is only newline at the end of each line, that is \ n;
    • On the MAC system, the end of each line is enter, that is, \ r. Unified with Linux starting with Mac OS X.

7.4 byte input stream [InputStream]

java.io.InputStream abstract class is a super class representing all classes of byte input stream, which can read byte information into memory. It defines the basic common function method of byte input stream.

  • public void close(): close this input stream and release any system resources associated with this stream.
  • public abstract int read(): reads the next byte of data from the input stream.
  • public int read(byte[] b): read some bytes from the input stream and store them in byte array B.

Tips:

close method, which must be called to release system resources when the operation of the stream is completed.

7.5 FileInputStream class

java. io. The FileInputStream class is a file input stream that reads bytes from a file.

Construction method

  • FileInputStream(File file): create a FileInputStream by opening a connection to the actual file, which is named by the file object file in the file system.
  • FileInputStream(String name): create a FileInputStream by opening a connection to the actual file, which is named by the pathname in the file system.

When you create a stream object, you must pass in a file path. If there is no such file in this path, FileNotFoundException will be thrown

  • For example, the code is as follows:
public class FileInputStreamConstructor throws IOException{
    public static void main(String[] args) {
   	 	// Create a stream object using a File object
        File file = new File("a.txt");
        FileInputStream fos = new FileInputStream(file);
      
        // Create a stream object with a file name
        FileInputStream fos = new FileInputStream("b.txt");
    }
}

Read byte data

  1. Read bytes: the read method can read one byte of data at a time, promote it to int type, read it to the end of the file, and return - 1. Code usage demonstration:
public class FISRead {
    public static void main(String[] args) throws IOException{
      	// Create a stream object with a file name
       	FileInputStream fis = new FileInputStream("read.txt");
      	// Read data and return a byte
        int read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
        read = fis.read();
        System.out.println((char) read);
      	// Read to the end and return - 1
       	read = fis.read();
        System.out.println( read);
		// close resource
        fis.close();
    }
}
Output results:
a
b
c
d
e
-1

Loop to improve the reading mode, code use demonstration:

public class FISRead {
    public static void main(String[] args) throws IOException{
      	// Create a stream object with a file name
       	FileInputStream fis = new FileInputStream("read.txt");
      	// Define variables and save data
        int b ;
        // Cyclic reading
        while ((b = fis.read())!=-1) {
            System.out.println((char)b);
        }
		// close resource
        fis.close();
    }
}
Output results:
a
b
c
d
e

Tips:

  1. Although a byte is read, it is automatically promoted to type int.
  2. After the stream operation is completed, you must release system resources and call the close method. Remember.
  1. Read using byte array: read(byte[] b). Each time the length of B is read into the array, return the number of valid bytes read. When reading to the end, return - 1. Code usage demonstration:
public class FISRead {
    public static void main(String[] args) throws IOException{
      	// Create a stream object with a file name
       	FileInputStream fis = new FileInputStream("read.txt"); // abcde in file
      	// Define variables as valid numbers
        int len ;
        // Defines a byte array as a container for byte data   
        byte[] b = new byte[2];
        // Cyclic reading
        while (( len= fis.read(b))!=-1) {
           	// After each reading, the array is changed into a string for printing
            System.out.println(new String(b));
        }
		// close resource
        fis.close();
    }
}

Output results:
ab
cd
ed

The error data d is because only one byte e is read during the last reading. In the array, the last read data has not been completely replaced, so it is necessary to obtain valid bytes through len. The code usage demonstration:

public class FISRead {
    public static void main(String[] args) throws IOException{
      	// Create a stream object with a file name
       	FileInputStream fis = new FileInputStream("read.txt"); // abcde in file
      	// Define variables as valid numbers
        int len ;
        // Defines a byte array as a container for byte data   
        byte[] b = new byte[2];
        // Cyclic reading
        while (( len= fis.read(b))!=-1) {
           	// After each reading, the valid byte part of the array is changed into a string for printing
            System.out.println(new String(b,0,len));//  len number of valid bytes read each time
        }
		// close resource
        fis.close();
    }
}

Output results:
ab
cd
e

Tips:

Array reading is used to read multiple bytes each time, which reduces the number of IO operations between systems, thus improving the efficiency of reading and writing. It is recommended to be used in development.

4.6 byte stream exercise: picture copying

Schematic diagram of replication principle

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-JhcTTsvI-1641466212752)(…/…/%E5%9F%BA%E7%A1%80%E5%8A%A0%E5%BC%BA%E8%AF%BE%E6%94%B9/day01-day15%E8%AE%B2%E4%B9%89-MD/day10-File%E7%B1%BB%E3%80%81%E9%80%92%E5%BD%92%E3%80%81%E5%AD%97%E8%8A%82%E6%B5%81%E3%80%81%E5%AD%97%E7%AC%A6%E6%B5%81/img/2_copy.jpg)]

Case realization

Copy the picture file and use the code to demonstrate:

public class Copy {
    public static void main(String[] args) throws IOException {
        // 1. Create flow object
        // 1.1 specifying data sources
        FileInputStream fis = new FileInputStream("D:\\test.jpg");
        // 1.2 designated destination
        FileOutputStream fos = new FileOutputStream("test_copy.jpg");

        // 2. Read and write data
        // 2.1 defining arrays
        byte[] b = new byte[1024];
        // 2.2 defining length
        int len;
        // 2.3 cyclic reading
        while ((len = fis.read(b))!=-1) {
            // 2.4 write data
            fos.write(b, 0 , len);
        }

        // 3. Close resources
        fos.close();
        fis.close();
    }
}

Tips:

Closing principle of flow: open first and then close, open later and close first.

Keywords: Java

Added by DaRkZeAlOt on Thu, 06 Jan 2022 12:59:41 +0200