1, Functional programming idea
1.1 concept
Object oriented thinking needs to focus on what objects are used to accomplish what. The idea of functional programming is similar to the function in our mathematics. It mainly focuses on what to do with the data.
Functional programming only focuses on the specific method body and parameter list, and nothing else
1.2 advantages
- Simple code and fast development
- Close to natural language and easy to understand
- Easy "concurrent programming"
2, Lambda expression
2.1 overview
Lambda yes)DK8 A grammar sugar in. It can be regarded as a syntax sugar, which can simplify the writing of some anonymous inner classes. It is an important embodiment of the idea of functional programming. Let's not focus on what it is. It's more about what we do with the data.
2.2 core principles
Derivable and omitted
2.3 basic format
()->{code}
Example 1
We can write anonymous inner classes when creating threads and starting them
new Thread(new Runable(){ @Override public void run() { System.out.println("HuDu"); } }).start();
You can modify it using Lambda's format. Revised as follows:
new Thread(()->{ System.out.println("HuDu"); }).start();
Example 2
The existing methods are defined as follows, where IntBinaryOperator is an interface. First call this method with the writing method of anonymous inner class. The parameter name of anonymous inner class can be modified at will.
public int calculateNum(IntBinaryOperator operator) { int a = 10; int b = 20; return operator.applyAsInt(a,b); } public void lambdaTest_2_1() { System.out.println(calculateNum(new IntBinaryOperator() { @Override public int applyAsInt(int left, int right) { return left + right; } })); }
Lambda:
public void lambdaTest_2_3() { System.out.println(calculateNum(Integer::sum)); }
Example 3
The existing methods are defined as follows, where intpredict is an interface. The method is called first using the writing method of the anonymous inner class.
public void printNum(IntPredicate predicate) { int[] arr = {1,2,3,4,5,6,7,8,9,10}; for (int i : arr) { if (predicate.test(i)) { System.out.println(i); } } } @Test public void lambdaTest_3_1() { printNum(new IntPredicate() { @Override public boolean test(int value) { return value %2 == 0; } }); }
Lambda writing
public void lambdaTest_3_3() { printNum((int value) -> 0 == value % 2); }
Example 4
The existing methods are defined as follows, where Function is an interface. First call this method using the writing method of the anonymous inner class.
public <R> R typeConvert(Function<String,R> function) { String str = "1235"; R result = function.apply(str); return result; } @Test public void lambdaTest_4_1() { System.out.println(typeConvert(new Function<String, Integer>() { @Override public Integer apply(String s) { return Integer.valueOf(s); } })); }
Lambda calling method
System.out.println(typeConvert((Function<String, Integer>) Integer::valueOf));
Example 5
The existing methods are defined as follows, where IntConsumer is an interface. First call this method using the writing method of the anonymous inner class.
public void foreachArr(IntConsumer consumer) { int[] arr = {1,2,3,4,5,6,7,8,9,10}; for (int i : arr) { consumer.accept(i); } } Anonymous inner class usage @Test public void lambdaTest_5_1() { foreachArr(new IntConsumer() { @Override public void accept(int value) { System.out.println(value); } }); }
lambda usage
Method reference foreachArr(System.out::println);
2.4 omission rules
- Parameter types can be omitted
- When the method body has only one sentence of code, the braces return and the semicolon of the only sentence of code can be omitted
- Parentheses can be omitted when the method has only one parameter
3, Stream stream
3.1 overview
Java8 of Stream It uses the functional programming pattern, just like its name, It can be used to perform chain flow operations on sets or arrays. It is more convenient for us to operate on sets or arrays.
3.2 case data preparation
@Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode // Used for later de duplication public class Author { private Long id; private String name; private Integer age; // brief introduction private String intro; private List<Book> books; }
@Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode public class Book { private Long id; private String name; private String category; private Integer score; private String intro; }
public List<Author> getAuthors() { // Data initialization Author author1 = new Author(1L, "Alice", 33, "test", null); Author author2 = new Author(2L, "Mike", 15, "test", null); Author author3 = new Author(3L, "Lucy", 14, "test", null); Author author4 = new Author(4L, "Lili", 14, "test", null); // Book list List<Book> books1 = new ArrayList<>(); List<Book> books2 = new ArrayList<>(); List<Book> books3 = new ArrayList<>(); books1.add(new Book(1L,"book1","category1,category2",88,"book1 intro")); books1.add(new Book(2L,"book2","category3,category2",99,"book2 intro")); books2.add(new Book(3L,"book3","category1",85,"book3 intro")); books2.add(new Book(3L,"book3","category1",85,"book3 intro")); books2.add(new Book(4L,"book4","category2,category4",56,"book4 intro")); books3.add(new Book(5L,"book5","category2",56,"book5 intro")); books3.add(new Book(6L,"book6","category4",100,"book6 intro")); books3.add(new Book(6L,"book6","category4",100,"book6 intro")); author1.setBooks(books1); author2.setBooks(books2); author3.setBooks(books3); author4.setBooks(books3); ArrayList<Author> authors = new ArrayList<>(Arrays.asList(author1, author2, author3, author4)); return authors; }
3.3. Quick start
3.3.1 requirements
We can call the getAuthors method to get the collection of writers. Now you need to print the names of all writers younger than 18, and pay attention to repetition.
3.3.2 realization
// Print the names of all writers younger than 18 and pay attention to de duplication List<Author> authors = getAuthors(); authors. stream() //Convert a collection into a stream .distinct() //Remove duplicate writers first .filter(author -> author.getAge() < 18) //Screening younger than 18 .forEach(author -> System.out.println(author.getName())); //Traversal print age less than 18
3.4 common operations
3.4.1. Create flow
Single column Collections: collection objects stream()
List<Author> authors = getAuthors(); Stream<Author> stream = authors.stream();
Arrays: arrays Stream (array) or use stream Of to create
Integer[] arr = {1,2,3,4,5}; Stream<Integer> stream = Arrays.stream(arr); // Static method call using Stream interface Stream<Integer> stream2 = Stream.of(arr);
Double column set: convert to single column before creating
Map<String,Integer> map = new HashMap<>(); map.put("Crayon Shin Chan",8); map.put("sunspot",12); map.put("HuDu",198); Stream<Map.Entry<String,Integer>> stream = map.entrySet.stream();
3.4.2 intermediate operation
filter
You can conditionally filter the elements in the stream. Only those that meet the filtering conditions can continue to stay in the stream.
// Print the names of all writers whose names are longer than 4 List<Author> authors = getAuthors(); authors.stream() .filter(author -> author.getName().length() > 4) .forEach(author -> System.out.println(author.getName()));
map
The elements in the convection can be calculated or converted.
// Print the names of all writers List<Author> authors = getAuthors(); authors.stream() .map(author -> author.getName()) .forEach(name -> System.out.println(name));
distinct
You can remove duplicate elements from the stream.
// Print the names of all writers, and there must be no duplicate elements List<Author> authors = getAuthors(); authors.stream() .distinct() .map(author -> author.getName()) .forEach(name -> System.out.println(name));
Note: the distinct method depends on the equals method of the Object to determine whether it is the same Object. Therefore, you need to pay attention to the equals method again.
sorted
You can sort elements in a stream.
// The elements in the convection are sorted in descending order by age, and there must be no duplicate elements List<Author> authors = getAuthors(); authors.stream() .distinct() .sorted() .map(author -> author.getName()) .forEach(name -> System.out.println(name)); List<Author> authors = getAuthors(); authors.stream() .distinct() // .sorted() .sorted((o1, o2) ->o1.getAge() - o2.getAge()) .map(author -> author.getName()) .forEach(name -> System.out.println(name));
Note: if the sorted() method of the null parameter is called, the element in the stream needs to implement Comparable.
@Data @NoArgsConstructor @AllArgsConstructor // Override the equals and hashcode methods @EqualsAndHashCode // Used for later de duplication public class Author implements Comparable<Author>{ private Long id; private String name; private Integer age; // brief introduction private String intro; private List<Book> books; /** * Compare itself to the incoming object * @param o Incoming object */ @Override public int compareTo(Author o) { return this.getAge() - o.getAge(); } }
limit
The maximum length of the stream can be set, and the excess will be discarded.
// The elements in the stream are sorted in descending order according to age, and it is required that there should be no duplicate elements, and then the names of the two oldest writers are printed. List<Author> authors = getAuthors(); authors.stream() .distinct() .sorted((o1, o2) ->o1.getAge() - o2.getAge()) .map(author -> author.getName()) .limit(2) .forEach(name -> System.out.println(name));
skip
Skip the first few elements in the stream
//Print writers other than the oldest writers. It is required that there should be no duplicate complex elements and they should be sorted in descending order of age. authors.stream() .distinct() .sorted((o1, o2) ->o1.getAge() - o2.getAge()) .map(author -> author.getName()) .skip(1) .forEach(name -> System.out.println(name));
flatMap
A map can only convert one object to another as an element in a stream. flatMap can convert an object into multiple objects as elements in the stream.
Example 1
// Print the names of all books and remove duplicate data List<Author> authors = getAuthors(); authors.stream() .flatMap(new Function<Author, Stream<Book>>() { @Override public Stream<Book> apply(Author author) { return author.getBooks().stream(); } }) .distinct() .forEach(new Consumer<Book>() { @Override public void accept(Book book) { System.out.println(book.getName()); } }); List<Author> authors = getAuthors(); authors.stream() .flatMap(author -> author.getBooks().stream()) .distinct() .forEach(book -> System.out.println(book.getName()));
Example 2
// When printing existing book types, formats like category1 and category2 cannot appear List<Author> authors = getAuthors(); authors.stream() .flatMap(author -> author.getBooks().stream()) .distinct() .flatMap(book -> Arrays.stream(book.getCategory().split(","))) .distinct() .forEach(category-> System.out.println(category));
3.4.3 termination operation
foreach
The traversal operation is performed on the meta cable in the stream. We specify what specific operation is performed on the traversed meta cable through the passed parameters.
// Output all writer names List<Author> authors = getAuthors(); authors.stream() .map(author -> author.getName()) .distinct() .forEach(name -> System.out.println(name));
count
It can be used to obtain the number of elements in the current money flow.
// Print the number of books published by these writers and go back long count = authors.stream() .flatMap(author -> author.getBooks().stream()) .distinct() .count();
max&min
It can be used to get the latest value in the stream
// Obtain the highest and lowest scores of the books produced by these writers respectively and print them. List<Author> authors = getAuthors(); Optional<Integer> max = authors.stream() .flatMap(author -> author.getBooks().stream()) .map(book -> book.getScore()) .max((o1, o2) -> o1 - o2); System.out.println(max.get()); List<Author> authors = getAuthors(); Optional<Integer> min = authors.stream() .flatMap(author -> author.getBooks().stream()) .map(book -> book.getScore()) .min((o1,o2)->o1-o2); System.out.println(min.get());
collect
Converts the current stream to a collection
// Gets a List collection that holds the names of all authors List<Author> authors = getAuthors(); List<String> nameList = authors.stream() .map(author -> author.getName()) .distinct() .collect(Collectors.toList());
// Gets a Set collection of all book titles List<Author> authors = getAuthors(); Set<Book> bookSet = authors.stream() .flatMap(author -> author.getBooks().stream()) .collect(Collectors.toSet());
// Get a map set, with the key of the map as the author name and the value as list < book > // Anonymous inner class writing List<Author> authors = getAuthors(); Map<String, List<Book>> map = authors.stream() .distinct() .collect(Collectors.toMap(new Function<Author, String>() { @Override public String apply(Author author) { return author.getName(); } }, new Function<Author, List<Book>>() { @Override public List<Book> apply(Author author) { return author.getBooks(); } })); // Slightly simplified expression List<Author> authors = getAuthors(); Map<String, List<Book>> map = authors.stream() .distinct() .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
Find and match
anyMatch
It can be used to judge whether there are any qualified elements, and the result is boolean type
// Judge whether there are writers over the age of 29 List<Author> authors = getAuthors(); boolean flag = authors.stream() .anyMatch(new Predicate<Author>() { @Override public boolean test(Author author) { return author.getAge() > 29; } }); reduce to List<Author> authors = getAuthors(); boolean flag = authors.stream() .anyMatch(author -> author.getAge() > 29);
allMatch
It can be used to judge whether they all meet the matching conditions. The result is of boolean type. If both match, the result is true, otherwise the result is false.
// Judge whether all writers are adults List<Author> authors = getAuthors(); boolean flag = authors.stream() .allMatch(author -> author.getAge() >= 18);
noneMatch
You can judge whether the elements in the flow do not meet the matching conditions, and the results do not meet the matching conditions. The result is true, otherwise it is false
// Judge whether no writer is over 100 years old. boolean flag = authors.stream() .noneMatch(author -> author.getAge() > 100);
findAny
Gets any element in the stream. This method has no way to ensure that the first element in the stream is obtained.
// Get any writer greater than 18, and print the name if it exists List<Author> authors = getAuthors(); Optional<Author> optionalAuthor = authors.stream() .filter(author -> author.getAge() > 18) .findAny(); optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
findFirst
Gets the first element in the stream
// Get the youngest writer and print the name List<Author> authors = getAuthors(); Optional<Author> optionalAuthor = authors.stream() .sorted((age1,age2)->age1.getAge()-age2.getAge()) .findFirst(); optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
reduce merge
The data in the convection calculates a result according to the calculation method you specify. (reduction operation)
The function of reduce is to combine the elements in the stream. We can pass in an initial value. It will calculate the elements and initialization values in the stream in turn according to our calculation method, and then calculate the calculation results with the following elements
The internal classes of the overload mode of the reduce two parameters are as follows:
T result = idnetity; for (T element : this stream) result = accumulator.apply(result, element) return result;
Among them, identity is the initial value that we can pass in through the method parameters. The application of the calculator is explained in detail, and the calculation is also determined through the method parameters.
example:
// Use reduce to sum the ages of all authors List<Author> authors = getAuthors(); Integer sum = authors.stream() .distinct() .map(author -> author.getAge()) .reduce(0, new BinaryOperator<Integer>() { @Override public Integer apply(Integer result, Integer element) { return result + element; } }); The simplified form is as follows List<Author> authors = getAuthors(); Integer sum = authors.stream() .distinct() .map(author -> author.getAge()) .reduce(0, (result, element) -> result + element);
// Use reduce to find the maximum age of all authors List<Author> authors = getAuthors(); Integer max = authors.stream() .map(author -> author.getAge()) .reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() { @Override public Integer apply(Integer result, Integer element) { return result < element ? element : result; } });
// Use reduce to find the minimum age of all authors List<Author> authors = getAuthors(); Integer min = authors.stream() .map(author -> author.getAge()) .reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
reduce the internal calculation of an overloaded form of a parameter
boolean foundAny = false; T result = null; for (T element : this stream) { if (!foundAny) { foundAny = true; result = element; } else result = accumulator.apply(result, element); } return foundAny ? Optional.of(result) : Optional.empty();
If the minimum value is obtained in the overloaded form of a parameter, the code is as follows
List<Author> authors = getAuthors(); Optional<Integer> minOptional = authors.stream() .map(author -> author.getAge()) .reduce(new BinaryOperator<Integer>() { @Override public Integer apply(Integer result, Integer element) { return result > element ? element : result; } }); minOptional.ifPresent(age -> System.out.println(age));
3.5 precautions
- Lazy evaluation (if there is no termination operation, no intermediate operation will not be executed)
- Stream one-time (once a stream object passes through a termination operation, the stream can no longer be used)
- It will not affect the original data (we can do a lot of processing with more data in the stream. But under normal circumstances, it will not affect the elements in the original set. This is often what we expect.)
4, Optional
4.1 overview
When we write code, the most common exception is null pointer exception. So in many cases, we need to make all kinds of non empty judgments.
For example:
Author author = getAuthor(); if(author != null) { System.out.println(author.getName()); }
Especially if the attribute in the object is still an object. This judgment will be more.
Too many judgment statements will make our code look bloated.
Therefore, Optional is introduced into JDK8. After you get into the habit of using Optional, you can write more elegant code to avoid null pointer exceptions.
Moreover, Optional is also used in many API s related to functional programming. If you don't use Optional, it will also affect the learning of functional programming.
4.2 use
4.2.1 creating objects
Optional is like a wrapper class, which can encapsulate our specific data inside the optional object. Then we use the encapsulated method in optional to operate the encapsulated number
It can be very elegant to avoid null pointer exceptions.
We generally use the Optional static method ofNullable to encapsulate the data into an Optional object. There is no problem whether the passed in parameter is null or not.
Author author = getAuthor(); Optional<Author> authorOptional = Optional.ofNullable(author);
You may find it troublesome to add a line of code to encapsulate the data. However, if the getAuthor method is modified so that its return value is encapsulated Optional, it will be much more convenient for us to use.
And in the actual development, many of our data are obtained from the database. Mybatis can and already supports Optional from version 3.5. We can return the dao method directly
The value type is defined as an Optional type, and MyBastis will encapsulate the data into an Optional object and return it. The packaging process does not need our own operation.
If you are sure that an object is not empty, you can use the Optional static method of to encapsulate the data into an Optional object
Author author = new Author(); Optional<Author> authorOptional = Optional.of(author);
However, it must be noted that if of is used, the parameters passed in must not be null. (what happens when you try to pass in null)
If the return value type of a method is Optional. If we find that the return value of a calculation is null after judgment, we need to encapsulate null into an Optional object for return. In this case, you can use the Optional static method empty to encapsulate.
Optional.empty()
4.2.2 safe consumption
After we get an Optional object, we must use the data in it. At this time, we can use its ifPresent method pair to consume the value.
This method will judge whether the encapsulated data is empty. When it is not empty, the specific consumption code will be executed. This makes it safer to use.
For example, the following wording gracefully avoids null pointer exceptions.
Optional<Author> optionalAuthor = Optional.ofNullable(getAuthor()); optionalAuthor.ifPresent(author1 -> System.out.println(author1.getName()));
4.2.3. Get value
If we want to get the value and process it ourselves, we can use the ge method, but it is not recommended. Because an exception will occur when the internal data of option is empty.
4.2.4. Safety acquisition value
If we expect to get the value safely. Instead of using the get method, we recommend using the following methods provided by Optional.
- orElseGet
Gets the data and sets the default value when the data is empty. If the data is not empty, the data can be obtained. If it is empty, the object will be created according to the parameters you pass in and returned as the default value.
Optional<Author> authorOptional = getAuthorOptional(); Author author = authorOptional.orElseGet(() -> new Author());
- orElseThrow
Get the data. If the data is not empty, you can get the data. If it is empty, an exception throw will be created according to the parameters you pass in.
Optional<Author> optionalAuthor = Optional.ofNullable(getAuthor()); try { Author author = optionalAuthor.orElseThrow(new Supplier<Throwable>() { @Override public Throwable get() { return new RuntimeException("author Empty"); } }); System.out.println(author.getName()); } catch (Throwable throwable) { throwable.printStackTrace(); } // Simplify as follows Optional<Author> optionalAuthor = Optional.ofNullable(getAuthor()); try { Author author = optionalAuthor.orElseThrow(() -> new RuntimeException("author Empty")); System.out.println(author.getName()); } catch (Throwable throwable) { throwable.printStackTrace(); }
4.2.5 filtering
We can use the filter method to filter the data. If there is data originally, but it does not meet the judgment, it will also become an Optional object without data.
Optional<Author> optionalAuthor = Optional.ofNullable(getAuthor()); Optional<Author> authorOptional = optionalAuthor.filter(author -> author.getAge() > 18); authorOptional.ifPresent(author -> System.out.println(author.getName()));
4.2.6 judgment
We can use isPresent method to judge whether there is data. If it is empty, the return value is false. If it is not empty, the return value is true. However, this method does not reflect the benefits of Optional, and ifPresent method is recommended.
Optional<Author> optionalAuthor = Optional.ofNullable(getAuthor()); if (optionalAuthor.isPresent()) { System.out.println(optionalAuthor.get().getName()); }
4.2.7 data conversion
Optional also provides a map that allows us to convert the data, and the converted data is packaged by optional to ensure our safety.
For example, we want to get a collection of writers' books.
Optional<Author> authorOptional = Optional.ofNullable(getAuthor()); Optional<List<Book>> books = authorOptional.map(author -> author.getBooks()); books.ifPresent(new Consumer<List<Book>>() { @Override public void accept(List<Book> books) { books.forEach(book -> System.out.println(book.getName())); } });
5, Functional interface
5.1 overview
An interface with only one abstract method is called a function interface
The functional interfaces of JDK are marked with @ functional interface annotation. However, whether the annotation is added or not, as long as there is only one abstract method in the interface, it is a functional interface.
5.2 common functional interfaces
Consumer consumer interface
According to the parameter list and return value type of the abstract method, we can consume the incoming parameters in the method. Commonly used in forEach() mediumFunction calculation conversion interface
According to the parameter list and return value type of the abstract method, we can calculate or convert the incoming parameters in the method and return the results. Common in map()Predict judgment interface
According to the parameter list and return value type of the abstract method, we can judge the passed parameter conditions in the method and return the judgment results. Common in filter()Supplier production interface
According to the parameter list and return value type of the abstract method, we can create objects in the method and return the created objects.
5.3 common default methods
- and
We may need to splice judgment conditions when we use Predicate. The and method is equivalent to using & & to splice two judgment conditions. Application scenarios are generally used when we define methods ourselves
// Print writers older than 17 and whose name length is greater than 1. List<Author> authors = getAuthors(); Stream<Author> authorStream = authors.stream(); authorStream.filter(new Predicate<Author>() { @Override public boolean test(Author author) { return author.getAge() > 17; } }.and(new Predicate<Author>() { @Override public boolean test(Author author) { return author.getName().length() > 3; } })).forEach(author -> System.out.println(author)); Simplify as follows List<Author> authors = getAuthors(); Stream<Author> authorStream = authors.stream(); authorStream.filter(((Predicate<Author>) author -> author.getAge() > 17).and(author -> author.getName().length() > 3)).forEach(author -> System.out.println(author));
Custom method printNumber
public void functionTest_1_3() { printNumber(new IntPredicate() { @Override public boolean test(int value) { return value % 2 == 0; } }, new IntPredicate() { @Override public boolean test(int value) { return value > 4; } }); } public void printNumber(IntPredicate predicate1,IntPredicate predicate2) { int[] arr = {1,2,3,4,5,6,7,8,9,10}; for (int i : arr) { if (predicate1.and(predicate2).test(i)) { System.out.println(i); } } } // Simplify as follows printNumber(value -> value % 2 == 0, value -> value > 4);
or
We may need to splice judgment conditions when using the predict interface. The or method is equivalent to using | to splice two judgment conditions.// Print writers who are older than 17 or whose names are less than 2 in length. List<Author> authors = getAuthors(); Stream<Author> authorStream = authors.stream(); authorStream.filter(((Predicate<Author>) author -> author.getAge() > 17).or(author -> author.getName().length() < 2)).forEach(author -> System.out.println(author));
negate
Methods in the predict interface. The negate method is equivalent to adding a before the judgment! Indicates negation
// Print writers who are not older than 17 List<Author> authors = getAuthors(); Stream<Author> authorStream = authors.stream(); authorStream.filter(new Predicate<Author>() { @Override public boolean test(Author author) { return !(author.getAge() > 17); } }).forEach(author -> System.out.println(author.getAge())); // Use negate authorStream.filter(((Predicate<Author>) author -> author.getAge() > 17).negate()).forEach(author -> System.out.println(author.getAge()));
6, Method reference
When we use lambda, if there are only - method calls in the method body (including construction methods), we can further simplify the code with method references.
6.1 recommended usage
When using lambda, we don't need to consider when to use method reference, which method to use, and what format the method | uses. After writing the lambda method, we only need to use the shortcut key to try whether it can be converted into a method reference when we find that the method body has only one line of code and is a method call.
When we use more method references, we can write method references directly slowly.
6.2 basic format
Class name or object name:: method name
6.3 grammar explanation (understanding)
6.3.1 reference static method
It's actually a static method that references a class
format
Class name::Method name
Use premise
If there is only one line of code in the method body when we rewrite the method, and this line of code calls the static method of a class, and we pass all the parameters in the abstract method to be rewritten into the static method in order, then we can reference the static method of the class.
For example:
After optimization
List<Author> authors = getAuthors(); Stream<Author> authorStream = authors.stream(); authorStream .map(author -> author.getAge()) .map(String::valueOf) .forEach(age -> System.out.println(age));
6.3.2 entity class method of reference object
format
Object name::Method name
Use premise
If there is only one line of code in the method body when we rewrite the method, and this line of code calls the member method of an object, and we pass all the parameters in the abstract method to be rewritten into the member method in order, then we can reference the instance method of the object
for example
After optimization
List<Author> authors = getAuthors(); Stream<Author> authorStream = authors.stream(); StringBuilder sb = new StringBuilder(); authorStream .map(author -> author.getName()) .forEach(sb::append);
6.3.3 instance method of reference object
format
Class name::Method name
Use premise
If there is only one line of code in the method body when we rewrite the method, and this line of code is the member method that calls the first parameter, and we pass all the remaining parameters in the abstract method to be rewritten into the member method in order, then we can reference the instance method of the class.
for example
public void MethodReferenceTest_3_1() { subAuthorName("radian", new UseString() { @Override public String use(String str, int start, int length) { return str.substring(start,length); } }); }
After optimization
public void MethodReferenceTest_3_2() { subAuthorName("radian", String::substring); }
6.3.4 constructor reference
If a line of code in the method body is a constructor, you can use the constructor reference
format
Class name::new
Use premise
If there is only one line of code in the method body when we rewrite the method, and this line of code calls the constructor of a class, and we pass all the parameters in the abstract method to be rewritten into the constructor in order, then we can reference the constructor.
For example:
List<Author> authors = getAuthors(); authors.stream() .map(author -> author.getName()) .map(name -> new StringBuilder(name)) .map(sb -> sb.append("hudu").toString()) .forEach(str -> System.out.println(str));
After simplification
List<Author> authors = getAuthors(); authors.stream() .map(author -> author.getName()) .map(StringBuilder::new) .map(sb -> sb.append("hudu").toString()) .forEach(System.out::println);
7. Advanced usage
7.1. Basic data type optimization
Many of the Stream methods we used earlier use generics. Therefore, the parameters and return values involved are reference data types.
Even if we operate on integers and decimals, we actually use their wrapper classes. The automatic packing and unpacking introduced in JDK5 make it as convenient as using basic data types when using the corresponding packing classes. But you must know that packing and unpacking must take time. Although this time consumption is very small. But when a large amount of data is repeatedly packed and unpacked, you can't ignore this time loss.
So in order to optimize the time consumption of this part. Stream also provides many methods specifically for basic data types.
For example: mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble, etc.
List<Author> authors = getAuthors(); authors.stream() .map(author -> author.getAge()) .map(age -> age + 10) .filter(age -> age > 18) .map(age -> age + 2) .forEach(System.out::println);
After optimization
List<Author> authors = getAuthors(); authors.stream() .mapToInt(Author::getAge) .map(age -> age + 10) .filter(age -> age > 18) .map(age -> age + 2) .forEach(System.out::println);
7.2 parallel flow
When there are a large number of elements in the flow, we can use parallel flow to improve the efficiency of operation. Real parallel flow is to assign tasks to multiple threads to complete. If we implement it in code, it will be very complex, and you are required to have sufficient understanding and understanding of concurrent programming. If we use Stream, we only need to modify the call of a method, and we can use parallel flow to help us implement it, so as to improve efficiency.
for example
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); stream .filter(num -> num > 5) .reduce(Integer::sum) .ifPresent(System.out::println);
After optimization
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); stream.parallel() .filter(num -> num > 5) .reduce(Integer::sum) .ifPresent(System.out::println);
The parallel method can convert a serial stream into a parallel stream, or you can directly use the parallel stream method. You can debug breakpoints through peek() to see which thread is executing.
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); stream.parallel() .peek(new Consumer<Integer>() { @Override public void accept(Integer num) { System.out.println(num+":"+Thread.currentThread().getName()); } }) .filter(num -> num > 5) .reduce(Integer::sum) .ifPresent(System.out::println);