New Features of JDK8 - Study Notes

Sparrow note link:
https://www.yuque.com/g/u22538081/ghlpft/zcbyis/collaborator/join?token=pofOuJabmo9rgKvS# Invite you to view the document "Functional Programming - New Features of jdk"

Why study?

Understand the company's code
High efficiency in processing collections in large quantities
High code readability
Eliminate nesting hell

concept

Object-oriented thinking needs to focus on what objects do what
, while functional programming thinking is similar to functions in our mathematics, it mainly focuses on what operations are performed on data.

advantage

Simple code, fast development
Close to natural language, easy to understand
Easy "concurrent programming"

lambda expression

concept

Syntactic sugar
Simplify the writing of some anonymous inner classes.
Focus more on what we do with the data.

core

Can be derived or omitted

basic format

(parameter list) —> {code}

example one

Previously, creating a thread to start an anonymous inner class

new Thread(new Runnable() {
    
    
    @Override
    public void run() {
    
    
        System.out.println("新线程中run方法被执行了");
    }
}).start();

After using lambda

		/**
		 * 什么时候可以用lambda简化呢?
		 * 	1、如果匿名内部类是一个接口
		 * 	2、并且这个接口中只有一个方法需要重写(有多个就不好判断重写哪个了)
		 * 	【关注参数和方法体】
		 */
		new Thread(()-> {
    
    
			System.out.println("lambda表达式");
		}).start();

Example 2

package lambda;

import java.util.function.IntBinaryOperator;

public class LambdaDemo01 {
    
    
	public static void main(String[] args) {
    
    
		/**
		 * 原来的写法
		 */
		int i = calculateNum(new IntBinaryOperator() {
    
    
			@Override
			public int applyAsInt(int left, int right) {
    
    
				// TODO Auto-generated method stub
				return left + right;
			}
		});
		System.out.println(i);
		
		/**
		 * 使用lambda表达式
		 */
		int j = calculateNum((int left,int right) -> {
    
    
			return left + right;
		});
		System.out.println(j);
	
	}
	//IntBinaryOperator 是函数式接口
	public static int calculateNum(IntBinaryOperator operator) {
    
    
		int a = 10;
		int b = 20;
		return operator.applyAsInt(a, b);
	}
}

Idea shortcut key, change to lambda
insert image description here

Idea shortcut key, lambda becomes original
insert image description here

Example three

package lambda;

import java.util.function.IntBinaryOperator;
import java.util.function.IntPredicate;

public class LambdaDemo01 {
    
    
	public static void main(String[] args) {
    
    
		/**
		 * 原来的写法
		 */
		printNum(new IntPredicate() {
    
    
			
			@Override
			public boolean test(int value) {
    
    
				// TODO Auto-generated method stub
				return value%2 == 0;
			}
		});
		
		System.out.println("==================");
		
		/**
		 * 使用lambda表达式
		 */
		printNum((int value) -> {
    
    
			return value%2 == 0;
		});
		
	}
	//IntPredicate 是一个函数式接口
	public static 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);
			}
		}
		
	}
	
}

Example four

package lambda;

import java.util.function.Function;

public class LambdaDemo01 {
    
    
	public static void main(String[] args) {
    
    

		/**
		 * 原来方式
		 */
		 Integer result = typeConver(new Function<String, Integer>() {
    
    

			@Override
			public Integer apply(String t) {
    
    
				// TODO Auto-generated method stub
				return Integer.valueOf(t);
			}
			
		});
		 
		System.out.println(result);
		
		/**
		 * 使用lambda表达式
		 */
		Integer result2 = typeConver((String t) -> {
    
    
			return Integer.valueOf(t);
		});
		System.out.println(result2);
		
	}
	//Function 是一个函数式接口	
	public static <R> R typeConver(Function<String,R>function) {
    
    
		String str = "1235";
		R result = function.apply(str);
		return result;
	}
	
}

Example five

package lambda;

import java.util.function.Function;
import java.util.function.IntConsumer;

public class LambdaDemo01 {
    
    
	public static void main(String[] args) {
    
    

		/**
		 * 原来方式
		 */
		foreachArr(new IntConsumer() {
    
    
			
			@Override
			public void accept(int value) {
    
    
				// TODO Auto-generated method stub
				System.out.println(value);
			}
		});
		 
		
		/**
		 * 使用lambda表达式
		 */
		foreachArr((int value) -> {
    
    
			System.out.println(value);
		});
	}
	//Function 是一个函数式接口	
	public static void foreachArr(IntConsumer consumer) {
    
    
		int[] arr = {
    
    1,2,3,4,5,6,7,8,9,10};
		for(int i : arr) {
    
    
			consumer.accept(i);
		}
	}
}

omitting rules

The parameter type can be omitted.
When the method body has only one line of code, the curly brace return and the semicolon of the only line of code can be omitted.
When the method has only one parameter, the parentheses can be omitted.
The above rules cannot be remembered or can be omitted.

omit example

package lambda;

import java.util.function.Function;
import java.util.function.IntConsumer;

public class LambdaDemo01 {
    
    
	public static void main(String[] args) {
    
    
		
		/**
		 * 使用lambda表达式
		 */
		foreachArr((int value) -> {
    
    
			System.out.println(value);
		});
		
		/**
		 * 省略后
		 */
		foreachArr(value -> System.out.println(value));
	}
	//Function 是一个函数式接口	
	public static void foreachArr(IntConsumer consumer) {
    
    
		int[] arr = {
    
    1,2,3,4,5,6,7,8,9,10};
		for(int i : arr) {
    
    
			consumer.accept(i);
		}
	}
}

Stream

1 Overview

Java8's Stream uses a functional programming model. Like its name, it can be used to perform chain-like stream operations on collections or arrays, making it easier for us to operate on collections or arrays.

2. Case data preparation

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
    </dependency>
</dependencies>

author class

package com.sangeng;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.List;

@Data   //生成get和set
@NoArgsConstructor  //无参构造
@AllArgsConstructor //有参构造
@EqualsAndHashCode  //用于后期去重使用(重写equals和hashCode方法)
public class Author {
    
       //作者

    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //作品
    private List<Book> books;
}

Books

package com.sangeng;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode//用于后期的去重使用
public class Book {
    
    //书
    //id
    private Long id;
    //书名
    private String name;
    //分类
    private String category; //哲学,小说
    //评分
    private Integer score;
    //简介
    private String intro;

}

data

package com.sangeng;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class StreamDemo {
    
    
    public static void main(String[] args) {
    
    

        List<Author> authors = getAuthors();
    }

    private static List<Author> getAuthors(){
    
    
        //数据初始化
        Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
        Author author2 = new Author(2L,"亚拉索",15,"狂风也追逐不上他的思考速度",null);
        Author author3 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
        Author author4 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);

        //书籍列表
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L,"刀的两侧是光明与黑暗","哲学,爱情",88,"用一把划分了爱情"));
        books1.add(new Book(2L,"一个人同一把刀","个人成长,爱情",99,"讲述如何从"));

        books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你去世界尽头"));
        books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你去世界尽头"));
        books2.add(new Book(4L,"吹或不吹","爱情,个人传记",56,"一个哲学家的爱情"));

        books3.add(new Book(5L,"刀的两侧是光明与黑暗","爱情",56,"无法想象一个武者"));
        books3.add(new Book(6L,"风与剑","个人传记",100,"讲述如何从"));
        books3.add(new Book(6L,"风与剑","个人传记",100,"讲述如何从"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);


        List<Author> authorsList = new ArrayList<>(Arrays.asList(author,author2,author3,author4));

        return authorsList;
    }
}

3. Quick start

Requirement:
We can call the getAuthors method to get the collection of authors. Now you need to print the names of all writers whose age is less than 18 , and pay attention to deduplication

accomplish:

package com.sangeng;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;

public class StreamDemo {
    
    
    public static void main(String[] args) {
    
    

        List<Author> authors = getAuthors();
        //System.out.println(authors);

        //打印所有年龄小于18的作家名字,并且要注意去重
        //filter过滤
        //forEach遍历
        authors.stream()    //stream()把集合转换成流
            .distinct()     //distinct()去重
            .filter(author -> author.getAge() < 18) //filter过滤【名字小于18】
            .forEach(author -> System.out.println(author.getName()));   //forEach遍历
    }

    private static List<Author> getAuthors(){
    
    
        //数据初始化
        Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
        Author author2 = new Author(2L,"亚拉索",15,"狂风也追逐不上他的思考速度",null);
        Author author3 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
        Author author4 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);

        //书籍列表
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L,"刀的两侧是光明与黑暗","哲学,爱情",88,"用一把划分了爱情"));
        books1.add(new Book(2L,"一个人同一把刀","个人成长,爱情",99,"讲述如何从"));

        books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你去世界尽头"));
        books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你去世界尽头"));
        books2.add(new Book(4L,"吹或不吹","爱情,个人传记",56,"一个哲学家的爱情"));

        books3.add(new Book(5L,"刀的两侧是光明与黑暗","爱情",56,"无法想象一个武者"));
        books3.add(new Book(6L,"风与剑","个人传记",100,"讲述如何从"));
        books3.add(new Book(6L,"风与剑","个人传记",100,"讲述如何从"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);


        List<Author> authorsList = new ArrayList<>(Arrays.asList(author,author2,author3,author4));

        return authorsList;
    }
}

Quick key operation

debug Stream style

Java content becomes method
insert image description here

4. Routine operation

1. Create a flow

Single-column collection : collection object.Stream

List<Author> authors = getAuthors();
Stream<Author> stream = authors.stream();

Array : Arrays.stream (array) or use Stream.of to create

Integer[] arr = {
    
    1,2,3,4,5};
Stream<Integer> stream1 = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);

Two-column collection : convert to a single-column collection and then create

Map<String,Integer> map = new HashMap<>();
map.put("垃圾公司",19);
map.put("黑子",17);
map.put("日向源",16);
Stream<Map.Entry<String,Integer>> stream3 = map.entrySet().stream();//entrySet()把map集合key,value封装成Set
//上一句分开
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Stream<Map.Entry<String, Integer>> stream4 = entrySet.stream();

2. Intermediate operation

filter filter

Elements in the stream can be filtered conditionally, and only those that meet the filter conditions can remain in the stream.

Example:
print the names of all writers whose name length is greater than 1

        //打印所有名字长度大于1的作家的姓名
        List<Author> authors = getAuthors();
        authors.stream()
                .filter(author -> author.getName().length() > 1)    //过滤 名字长度大于1
                .forEach(author -> System.out.println(author.getName()));

map

You can calculate or transform the elements in the convection stream

Example:
print all writer names

        // map 打印所有作家的名字
        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())    //获取所有名字
                .forEach(name -> System.out.println(name));

        //map 所有年龄加10
        authors.stream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .forEach(age -> System.out.println(age));

distinct

Duplicate elements can be removed

For example:
print the names of all authors, and require that there should be no repeated elements.
Note: The distinct method relies on the equals method of Object to determine whether they are the same object. So pay attention to rewriting the equals method

        //打印所有作家的名字,并且要求其中不能有重复元素
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct() //去重
                .forEach(author -> System.out.println(author.getName()));

sorted

elements in a stream can be sorted

For example:
Sort the elements in the stream in descending order according to age, and require no duplicate elements
Note: If you call the sorted() method with empty parameters, the elements in the stream need to implement the Comparable interface

 //对流中的元素按照年龄进行降序排序,并且要求不能有重复元素
 List<Author> authors = getAuthors();
 authors.stream()
         .sorted() //排序 无参sorted()使用排序需要实现Comparable排序接口
         .distinct() //去重
         .forEach(author -> System.out.println(author.getName()));

 authors.stream()
         .distinct()
         .sorted((o1,o2) -> o2.getAge() - o1.getAge())   //排序 有参sorted直接匿名内部类实现Comparable排序接口
         .forEach(author -> System.out.println(author.getName()));

No parameter sorted, the elements in the stream need to implement the Comparable interface

package com.sangeng;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.List;

@Data   //生成get和set
@NoArgsConstructor  //无参构造
@AllArgsConstructor //有参构造
@EqualsAndHashCode  //用于后期去重使用(重写equals和hashCode方法)
public class Author implements Comparable<Author>{
    
       //作者

    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //作品
    private List<Book> books;

    @Override
    public int compareTo(Author o) {
    
    
        return this.getAge() - o.getAge();
    }
}

limit

The maximum length of the stream can be set, and the excess part will be discarded.
For example:
sort the elements in the stream in descending order according to age, and require that there should be no duplicate elements, and then print the names of the two oldest writers

//对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素,然后打印其中年龄最大的两个作家名字
List<Author> authors = getAuthors();
authors.stream()
    .distinct()// 去重
    .sorted((o1, o2) -> o2.getAge() - o1.getAge()) //排序,倒序
    .limit(2)   //设置流的最大长度,超出的部分将被抛弃
    .forEach(author -> System.out.println(author.getName() +" " +author.getAge()));

skip

Skip the first n elements in the stream and return the remaining elements
For example:
print other writers except the oldest writer, require no duplicate elements, and sort in descending order of age

 //打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序
 List<Author> authors = getAuthors();
 authors.stream()
         .distinct() //去重
         .sorted((o1, o2) -> o2.getAge() - o1.getAge())  //降序排序
         .skip(1)    //skip 跳过流中的前n个元素,返回剩下的元素
         .forEach(author -> System.out.println(author.getName() +" " +author.getAge()));

flatMap

map can only convert one object into another object as an element in the stream. And flatMap can convert an object into multiple objects as elements in the stream.
Example 1
Print the names of all books and require duplicate elements to be deduplicated.

 //map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
 //打印所有书籍的名字,要求对重复的元素进行去重。
 List<Author> authors = getAuthors();
 authors.stream()
         .flatMap(author -> author.getBooks().stream())  // 把获取到的所有一个个的书籍对象再转为流
         .distinct() //去重
         .forEach(book -> System.out.println(book));

 System.out.println("==========");
 authors.stream()
         .map(author -> author.getBooks())// 使用map的话,一个作者的书籍返回一个集合,不是一个个的书籍对象。
         .distinct()
         .forEach(book -> System.out.println(book));

Example 2
Print all classifications of the existing data, requiring deduplication of classifications, and this format cannot appear: philosophy, love.

//flatMap 打印现有数据的所有分类,要求对分类进行去重,不能出现这种格式:哲学,爱情。
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. End operation

forEach

To traverse the elements in the stream, we pass in parameters to specify what specific operations will be performed on the traversed elements
Example:
output the names of all writers

//forEach 输出所有作家的名字
List<Author> authors = getAuthors();
authors.stream()
    .map(author -> author.getName())
    .distinct()
    .forEach(name -> System.out.println(name));

count

Can be used to get the number of elements in the current stream
Example:
print the number of books published by these authors, pay attention to delete duplicate elements

//count 打印这些作家的所出书籍的数目,注意删除重复元素
List<Author> authors = getAuthors();
long count = authors.stream()
    .flatMap(author -> author.getBooks().stream())
    .distinct() //书信息进行去重
    .map(book -> book.getName())
    .distinct() //书名进行去重(由于我的测试数据,书名相同,但是有些分类写的是不同的)
    .count();   //计算 所有书的个数
System.out.println(count);

//老师这个【可能是没想到书名相同,但是分类没相同,这个是测试数据没写好】
long count1 = authors.stream()
    .flatMap(author -> author.getBooks().stream())
    .distinct() //书信息进行去重
    .count();
System.out.println(count1);

max min

It can be used to obtain the highest value in the stream.
Example:
obtain the highest and lowest scores of the books published by these authors and print them.

//max min 分别获取这些作家的所出书籍的最高分和最低分并打印。
List<Author> authors = getAuthors();
Optional max = authors.stream()
    .flatMap(author -> author.getBooks().stream())
    .map(book -> book.getScore() )
    .max((score1,score2) -> score1-score2);
System.out.println("最高评分 " + max.get());

Optional min = authors.stream()
    .flatMap(author -> author.getBooks().stream())
    .map(book -> book.getScore() )
    .min((score1,score2) -> score1-score2);
System.out.println("最低评分 " + min.get());

collect

Convert the current stream into a collection
Collectors.toList()
Collectors.toSet()
Collectors.toMap() [key and value]
Example:
Get a List collection that stores the author's name.

//collect
//获取一个存放说有作者名字的List集合。
List<Author> authors = getAuthors();
List<String> name = authors.stream()
    .map(author -> author.getName())
    .distinct()
    .collect(Collectors.toList());// 把流转换成List
System.out.println(name);

Get a Set collection of all book titles

// 获取一个所有书名的Set集合。
Set<String> bookName = authors.stream()
    .flatMap(author -> author.getBooks().stream())
    .distinct()
    .map(book -> book.getName())
    .collect(Collectors.toSet());
System.out.println(bookName);

Get a map collection, the key of the map is the author's name, and the value is a List [to write two functions, key and value]

// 获取一个map集合map的key为作者名,value为List<Book> 【map的话就要写两个方法】
Map<String,List<Book>> collect = authors.stream()
    .distinct()
    //map 两个返回值用“,”隔开(返回key,返回value)
    .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
System.out.println(collect);

Find and match anyMatch, allMatch, noneMatch

----------------match---------------

anyMatch

It can be used to judge whether there are any elements that meet the matching conditions, and the result is a Boolean type.
Example:
to judge whether there are writers over the age of 29

//anyMatch可以用来判断是否有任意符合匹配条件的元素,结果为Boolean类型
//例子:判断是否有年龄在29岁以上的作家
List<Author> authors = getAuthors();
Boolean test = authors.stream()
    .anyMatch(author -> author.getAge() > 29);
System.out.println(test);

allMatch

It can be used to judge whether all match conditions are met, and the result is a Boolean type. If all match the result is true, otherwise it is false
Example:
Determine whether all writers are adults

 //allMatch可以用来判断是否都符合匹配条件,结果为Boolean类型。如果都匹配结果为true,否则为false
 //例子:判断是否所有作家都成年了
 Boolean test2 = authors.stream()
         .allMatch(author -> author.getAge()>18);
 System.out.println(test2);
noneMatch

It can be judged whether the elements in the flow do not meet the matching conditions, if they do not meet the result, the result is true, otherwise it is false
Example:
to judge whether the writer is not over 100 years old

 //noneMatch可以判断流中的元素是否都不符合匹配条件,如果都不符合结果为true,否则为false
 //例子:判断作家是否都没超过100岁
 Boolean test3 = authors.stream()
         .noneMatch(author -> author.getAge()>=100);
 System.out.println(test3);

----------------Find---------------

findAny

Get any element in the stream. This method does not guarantee that the first element in the stream must be obtained.
Example:
Get any writer greater than 18, and output his name if it exists

 //findAny 获取流中的任意一个元素。该方法没有保证获取的一定是流中的第一个元素。
 //例子:获取任意一个大于18的作家,如果存在就输出他的名字
 List<Author> authors = getAuthors();
 Optional<Author> optionalAny = authors.stream()
         .filter(author -> author.getAge() > 18)
         .findAny();//获取流中的任意满足条件的一个元素

 //有问题,年龄改成0,还是同一个人(不是说是随机的吗?怎么还是第一个,是我代码有问题,还是概率问题)
 optionalAny.ifPresent(author -> System.out.println(author.getName()));
        
findFirst

Get the first element in the stream.
Example:
Get the youngest writer and output his name

//findFirst 获取流中的第一个元素。
//例子:获取一个年龄最小的作家,并输出他的姓名
List<Author> authors = getAuthors();
Optional<Author> first = authors.stream()
    .sorted((o1, o2) -> o1.getAge() - o2.getAge())
    .findFirst();

first.ifPresent(author -> System.out.println(author.getName()));

reduce merge

Convect the data in the stream to calculate a result (reduction operation)
according to the calculation method you specify The element and the initialization value are calculated, and the calculation result is calculated with the following elements.
The internal calculation of the overloaded form of the two parameters of reduce is as follows:

T result = identity;
for(T element : this stream)
    result = accumulator.apply(result,element)
return result;

The internal calculation method of the overloaded form of reduce is as follows: assign the first element to the initial value, and then calculate

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();

Among them, identity is the initial value that we can pass in through the method parameters, and what calculation is performed by the apply of the accumulator is also determined by us through the method parameters.

//reduce简单理解(模拟)
int[] arr = {
    
    1,2,3,4};
int resut = 0;// 初始值
for (int i : arr){
    
      
    resut = resut + 1;// 具体操作
}
System.out.println(resut);

Example:
Use reduce to find the age sum of all authors

//使用reduce求所有作者的年龄和
List<Author> authors = getAuthors();
Integer sum = authors.stream()
    .distinct() // 去重
    .map(author -> author.getAge()) //  获取所有年龄
    // (初始值,(附上初始值,要操作的元素))
    .reduce(0, (result, element) -> result + element);
System.out.println(sum);

Use reduce to find the maximum age among the authors

 //使用reduce求所作者中年龄的最大值
 List<Author> authors = getAuthors();
 Integer max = authors.stream()  //其实max() 是封装好的reduce
         .distinct()
         .map(author -> author.getAge()) // 获取年龄
         //      初始值(最小值)      附上初始值,要操作的元素 -> 三目,算出最大值
         .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
 System.out.println(max);

Use reduce to find the minimum age among all authors

//使用reduce求所有作者中年龄的最小值
List<Author> authors = getAuthors();
Integer min = authors.stream()
    .distinct()
    .map(author -> author.getAge())
    //      初始值(最大值)      附上初始值,要操作的元素 -> 三目,算出最小值
    .reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);

  /*
  Optional<Integer> min = authors.stream().distinct().map(Author::getAge).reduce(Integer::min);
  System.out.println(min);
  */
System.out.println(min);

Use reduce in the form of one parameter to find the minimum age among all authors

// reduce一个参数的用法
//使用reduce求所有作者中年龄的最小值
List<Author> authors = getAuthors();
Optional<Integer> minOptional = authors.stream()
    .map(author -> author.getAge())
    //reduce一个参数,把第一个元素赋给初始值
    .reduce((result, element) -> result > element ? element : result);

minOptional.ifPresent(age -> System.out.println(age));

Stream Notes

Lazy evaluation (if there is no finalization operation, no intermediate operations will not be executed)
Streams are disposable (once a stream object undergoes a finalization operation, the stream cannot be used again) Does
not affect the original data (we You can do a lot of processing on the data in the stream, but under normal circumstances, it will not affect the elements in the original collection, which is often what we expect)

Optional

Overview:

When we write code, the most common occurrence is the null pointer exception. So in many cases we need to make various non-empty judgments
. For example:

List<Author> author = getAuthor();
 if (author != null){
    
    
     System.out.println(author.getName);
 }

·Especially when the properties in the object are not yet an object. This kind of judgment will be more
. Too many judgment statements will make our code bloated
. Therefore, Optional is introduced in JDK8. After you develop the habit of using Optional, you can write more elegant code to avoid null pointer exceptions .
·And Optional is also used in many APIs related to functional programming. If you don't know how to use Optional, it will also affect the learning of functional programming.

Optional use

create object

Optional is like a wrapper class, which can encapsulate our specific data inside Optional, and then we can use the encapsulated methods in Optional to manipulate the encapsulated data to avoid null pointer exceptions very elegantly.

1. We generally use the Optional static method ofNullable to encapsulate data into an Optional object, no matter whether the incoming parameter is null or not, there will be no problem .
focus

   Author author = getAuthor();
   Optional<Author> authorOptional = Optional.ofNullable(author);

all

package com.sangeng;

import java.util.Optional;
import java.util.function.Consumer;

public class OptionalDemo {
    
    
    public static void main(String[] args) {
    
    
        Author author = getAuthor();
        Optional<Author> authorOptional = Optional.ofNullable(author);
        // ifPresent如果存在(如果数据为null,这里是不会被消费的)
        authorOptional.ifPresent(author1 -> System.out.println(author1.getName()));
    }

    public static Author getAuthor(){
    
    
        Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
        return author;// return null;
    }
}

You may think it is troublesome to add a line of code to encapsulate the data, but if we modify the getAuthor method so that its return value is the encapsulated Optional, it will be much more convenient for us to use. (Recommended Use)

package com.sangeng;

import javax.swing.text.html.Option;
import java.util.Optional;
import java.util.function.Consumer;

public class OptionalDemo {
    
    
    public static void main(String[] args) {
    
    
        Optional<Author> authorOptional = getAuthorOptional();
        authorOptional.ifPresent(author -> System.out.println(author.getName()));
    }

    public static Optional<Author> getAuthorOptional(){
    
    
        Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
        return Optional.ofNullable(author);//直接返回一个Optional
    }
}

And in actual development, a lot of our data is obtained from the database. Mybatis has been able to support Optional since version 3.5. We can directly define the return value type of the dao method as an Optional type, and MyBatis will encapsulate the data into an Optional object and return it. The encapsulation process does not require our own operation.

If you are sure that the object is not empty , you can use the Optional static method of to encapsulate the data into an Optional object (not recommended
)

 Author author = getAuthor();
 Optional<Author> author1 = Optional.of(author);
package com.sangeng;

import javax.swing.text.html.Option;
import java.util.Optional;
import java.util.function.Consumer;

public class OptionalDemo {
    
    
    public static void main(String[] args) {
    
    

        Author author = getAuthor();
        Optional<Author> author1 = Optional.of(author);
        author1.ifPresent(author2 -> System.out.println(author2.getName()));

    }


    public static Author getAuthor(){
    
    
        Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
        return author;//return null;
    }
}

It must be noted that if you use of, the parameters passed in must not be null. (Try to pass in null and what will happen)
Use the of method, if the data is empty, a null pointer exception java.lang.NullPointerException will be reported

If a method return value type is Optional type. And if we judge and find that the return value of a certain calculation is null, then we need to encapsulate null into an Optional object and return it. At this time, you can use the optional static method empty to encapsulate.

Optional.empty();
public static Optional<Author> getAuthorOptional(){
    
    
    Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
    return author == null?Optional.empty():Optional.of(author);
}

Which do you think is more convenient?
I will use Optional.ofNullable() in the future

safe consumption

After we get an Optional object, we definitely need to use the data in it. At this time, we can use its ifPresent method to consume the value. This method will judge whether the data encapsulated in it is empty, and only execute the specific consumption code when it is not empty. This is much safer to use.
For example,
the following writing method elegantly avoids the null pointer exception.

 Optional<Author> author1 = Optional.of(author);
 author1.ifPresent(author2 -> System.out.println(author2.getName()));

get value

If we want to get the value and process it ourselves, we can use the get method to get it, but it is not recommended. Because an exception occurs when the data inside the Optional is empty.
Using the get method, if the data is empty, an exception java.util.NoSuchElementException: No value present will be reported

Optional<Author> author = getAuthorOptional();
Author author1 = author.get();

Get the value safely

If we expect to get the value safely. We do not recommend using the get method, but use the following methods provided by Optional.
orElseGet
gets 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, an object will be created according to the parameters you passed in as the default value.
The key point

Optional<Author> authorOptional = getAuthorOptional();
//如果有值,就返回Author。如果没有值就返回默认值orElseGet(设置默认值)
Author author = authorOptional.orElseGet(() -> new Author());
Optional<Author> authorOptional = getAuthorOptional();
//如果有值,就返回Author。如果没有值就返回默认值orElseGet(设置默认值)
Author author = authorOptional.orElseGet(() -> new Author(2L,"大雨",33,"一个从菜刀中明悟哲理的祖安人",null));
System.out.println(author.getName());

public static Optional<Author> getAuthorOptional(){
    
    
    Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
    //Author author = null;
    return author == null?Optional.empty():Optional.of(author);
}

orElseThrow
gets the data, if the data is not empty, the data can be obtained. If it is empty, an exception is thrown based on the parameters you pass in .
teacher template

Optiona1<Author> authoroptiona1 = Optional.ofNu1lable(getAuthor 0));
try {
    
    
    Author author = authoroptional.orElseThrow((Supplier<Throwable>) () ->
                                                new RuntimeException("author为空"));
    System.out .printIn(author .getName());
} catch (Throwable throwable) {
    
    
    throwable.printstackTrace();
};

combat test

Optional<Author> authorOptional = getAuthorOptional();
try {
    
    
    //如果author,就输出author。如果为空,就抛出异常。
    Author author = authorOptional.orElseThrow(() -> new RuntimeException("数据为null"));
    System.out.println(author);
} catch (Throwable throwable) {
    
    
    throwable.printStackTrace();
}

public static Optional<Author> getAuthorOptional(){
    
    
    Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
    //Author author = null;
    return author == null?Optional.empty():Optional.of(author);
}

filter

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> authorOptional = getAuthorOptional();
authorOptional.filter(author -> author.getAge() > 18)
    .ifPresent(author -> System.out.println(author.getName()));

public static Optional<Author> getAuthorOptional(){
    
    
    Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
    //Author author = null;
    return author == null?Optional.empty():Optional.of(author);
}

judgment

We can use the isPresent method to judge whether there is data. If it is empty, the return value is false, and if it is not empty, the return value is true. However, this method does not reflect the benefits of Optional, and it is recommended to use the ifPresent method

Optional<Author> authorOptional = getAuthorOptional();
if (authorOptional.isPresent()) {
    
    
    System.out.println(authorOptional.get().getName());
}

data conversion

Optional also provides a map that allows us to convert the data, and the converted data is still packaged by Optional, ensuring our safe use. For example, we want to get the collection of books by authors
.

Optional<Author> authorOptional = getAuthorOptional();
authorOptional.map(author -> author.getBooks())
    .ifPresent(books -> System.out.println(books));

Optional<Author> authorOptional = getAuthorOptional();
        Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());
        optionalBooks.ifPresent(books -> System.out.println(books));

Summarize

创建Optional:ofNullable
Optional authorOptional = Optional.ofNullable(author);
安全消费:ifPresent
authorOptional.ifPresent(author2 -> System.out.println(author2.getName()));

functional interface

overview

An interface with only one abstract method is called a functional interface.
The functional interfaces of the JDK are marked with **@FunctionallInterface** annotations. But no matter whether the annotation is added or not, as long as there is only one abstract method in the interface, it is a functional interface.

Common Functional Interface

According to the parameter list and return value type of the abstract method in the Consumer consumption interface
, we can consume the incoming parameters in the method.

@FunctionalInterface
public interface Consumer<T> {
    
    

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
    
    
        Objects.requireNonNull(after);
        return (T t) -> {
    
     accept(t); after.accept(t); };
    }

Function 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 result

@FunctionalInterface
public interface Function<T, R> {
    
    

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

    /**
     * Returns a composed function that first applies the {@code before}
     * function to its input, and then applies this function to the result.
     * If evaluation of either function throws an exception, it is relayed to
     * the caller of the composed function.
     *
     * @param <V> the type of input to the {@code before} function, and to the
     *           composed function
     * @param before the function to apply before this function is applied
     * @return a composed function that first applies the {@code before}
     * function and then applies this function
     * @throws NullPointerException if before is null
     *
     * @see #andThen(Function)
     */
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    
    
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

Predicate judgment interface
According to the parameter list and return value type of the abstract method, we can judge the incoming parameter conditions in the method and return the judgment result

@FunctionalInterface
public interface Predicate<T> {
    
    

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    /**
     * Returns a composed predicate that represents a short-circuiting logical
     * AND of this predicate and another.  When evaluating the composed
     * predicate, if this predicate is {@code false}, then the {@code other}
     * predicate is not evaluated.
     *
     * <p>Any exceptions thrown during evaluation of either predicate are relayed
     * to the caller; if evaluation of this predicate throws an exception, the
     * {@code other} predicate will not be evaluated.
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
    
    
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

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 object

@FunctionalInterface
    public interface Supplier<T> {
    
    

        /**
        * Gets a result.
        *
        * @return a result
        */
        T get();
    }

![image.png](https://img-blog.csdnimg.cn/img_convert/b8949435efdda633d4b4c8fbd258902b.png#averageHue=#f3f3dc&clientId=u3b1cbb5a-c114-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=251&id=ua0206448&margin=[object Object]&name=image.png&originHeight=282&originWidth=1001&originalType=binary&ratio=1&rotation=0&showTitle=false&size=177121&status=done&style=none&taskId=u70a12f12-cc60-4b11-ba18-a6c487dedb0&title=&width=889.7777777777778)

common default method

And
when we use the Predicate interface, we may need to splice the judgment conditions. The and method is equivalent to using && to splice two judgment conditions
. For example:
print writers whose age is greater than 17 and whose name length is greater than 1.

//打印作家中年龄大于17并且姓名的长度大于1的作家。
List<Author> authors = getAuthors();
authors.stream()
    .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() > 1;
        }
    }))
    .forEach(author -> System.out.println(author));

Or
when we use the Predicate interface, we may need to splice the judgment conditions. The or method is equivalent to using to splice two judgment conditions.
For example:
print writers whose age is greater than 17 or whose name length is less than 2.

//打印作家中年龄大于17或者姓名的长度小于2的作家。
List<Author> authors = getAuthors();
authors.stream()
    .filter(new Predicate<Author>() {
    
    
        @Override
        public boolean test(Author author) {
    
    
            return author.getAge()>17;
        }
    }.or(new Predicate<Author>() {
    
    
        @Override
        public boolean test(Author author) {
    
    
            return author.getName().length() < 2;
        }
    })).forEach(author -> System.out.println(author.getName()));

negate
the method in the Predicate interface. The negate method is equivalent to adding a ! in front of the judgment addition to indicate negation
. For example:
print writers whose age is not older than 17.

//打印作家中年龄不大于17的作家。
List<Author> authors = getAuthors();
authors.stream()
    .filter(new Predicate<Author>() {
    
    
        @Override
        public boolean test(Author author) {
    
    
            return author.getAge() > 17;
        }
    }.negate()).forEach(author -> System.out.println(author));

method reference

When we use lambda, if there is only one method call in the method body (including the construction method), we can use method references to further simplify the code.

recommended method

When we use lambda, we don't need to consider when to use method references, which method references to use, and what the format of method references is. We only need to use shortcut keys to try whether it can be converted into a method reference when we find that the method body has only one line of code after writing the lambda method, and it is a method call.
When we use more method references, we can slowly write method references directly.

basic format

class or object name::method name

Detailed Grammar

referencing a static method

In fact, it is the static method
format of the reference class

类名::方法名

Prerequisites If we rewrite a method, there is only one line of code
in the method body , and this line of code calls a static method of a certain class , and we pass in all the parameters in the abstract method to be rewritten in order In this static method , at this time we can refer to the static method of the class. For example: the following code can be simplified with method references

List<Author> authors = getAuthors();
authors.stream()
    .map(author -> author.getAge())
    .map(age -> String.valueOf(age));

Note that if the method we rewrite has no parameters, the called method also has no parameters, which is equivalent to complying with the above rules.
After optimization is as follows:

List<Author> authors = getAuthors();
authors.stream()
    .map(author -> author.getAge())
    .map(String::valueOf);

Instance method of reference object

Format

对象名::方法名

Prerequisites
If we rewrite a method, there is only one line of code in the method body , and this line of code calls a member method of an object , and we pass in all the parameters in the abstract method to be rewritten in order In this member method , we can refer to the instance method of the object at this time
. For example:

List<Author> authors = getAuthors();
Stream<Author> authorstream = authors .stream();
StringBuilder sb = new StringBuilder();
authorstream.map(author -> author.getName())
    .forEach(name->sb.append(name));

Optimized:

List<Author> authors = getAuthors();
Stream<Author> authorstream = authors .stream();
StringBuilder sb = new StringBuilder();
authorstream.map(author -> author.getName())
    .forEach(sb::append);

Instance method of reference class

Format

类名::方法名

If
we rewrite the method, there is only , and this line of code calls the member method of the first parameter , and we put all the remaining parameters in the abstract method to be rewritten They are all passed into this member method in order , and at this time we can refer to the instance method of the class.
For example:

package com.sangeng;

import java.util.List;

public class MethodDemo {
    
    
    interface UseString{
    
    
        String use(String str,int start,int length) ;
    }


    public static String subAuthorName(String str, UseString useString) {
    
    
        int start = 0;
        int length = 1;
        return useString.use(str, start, length);

    }
    public static void main(String[] args) {
    
    
        subAuthorName("三更草堂",new UseString() {
    
    
            @Override
            public String use(String str, int start, int length) {
    
    
                return str.substring(start,length);
            }
        });

    }

}

Optimized:

package com.sangeng;

import java.util.List;

public class MethodDemo {
    
    
    interface UseString{
    
    
        String use(String str,int start,int length) ;
    }


    public static String subAuthorName(String str, UseString useString) {
    
    
        int start = 0;
        int length = 1;
        return useString.use(str, start, length);

    }
    public static void main(String[] args) {
    
    
        subAuthorName("三更草堂", String::substring);

    }

}

constructor reference

Constructor references can be used if one line of code in the method body is a constructor.
Format

类名::new

If
we rewrite the method, there is only one line of code in the method body , and this line of code calls the constructor of a certain class , and we pass all the parameters in the abstract method to be rewritten in order Into this construction method , we can refer to the constructor at this time.
For example:

List<Author> authors = getAuthors();
authors.stream()
    .map(author -> author.getName())
    .map(name -> new StringBuilder(name))
    .map(sb -> sb.append("-三更").toString())
    .forEach(str -> System.out.println(str));

Optimized:

List<Author> authors = getAuthors();
authors.stream()
    .map(author -> author.getName())
    .map(StringBuilder::new)
    .map(sb -> sb.append("-三更").toString())
    .forEach(str -> System.out.println(str));

advanced usage

Basic data type optimization

Many of the Stream methods we used before used generics. So the parameters involved and the return value are all reference data types.
Even if we operate integers and decimals, we actually use their wrapper classes. The automatic boxing and automatic unboxing introduced in JDK5 make it as convenient for us to fix and use basic data types when using the corresponding packaging classes. But you must know that boxing and unboxing will definitely consume time. Although this time consumption is very low. But when a large amount of data is repeatedly boxed and unboxed, you can't ignore this time loss.
So in order to allow us to optimize this part of the time consumption. Stream also provides a number of methods specific to primitive data types.

For example: madTolnt, mapToLong, mapToDouble, flatMapTolnt, 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);

        //优化,(自动装箱和拆箱是要消耗时间的)
        authors.stream()
                .mapToInt(author -> author.getAge())// 拆箱一次,Integer->int
                .map(age -> age +10) // 后面的都是int了,不用自动拆箱了。
                .filter(age -> age>18)
                .map(age -> age+2)
                .forEach(System.out::println);

parallel stream

We can use parallel streams to improve the efficiency of operations when there are a large number of elements in the stream. In fact, parallel flow is to assign tasks to multiple threads to complete. If we use code to implement it ourselves, it will actually be very complicated, and requires you to have sufficient understanding and knowledge of concurrent programming. And if we use Stream, we only need to modify the call of a method to use parallel streams to help us achieve it, thereby improving efficiency.

The parallel method can convert a serial stream into a parallel stream

Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9,10);
/*
Integer integer = stream.filter(num -> num > 5)
.reduce((result, element) -> result + element)
.get();
System.out.println(integer);
*/

//变成并行流(多线程完成操作)
Integer integer2 = stream.parallel() // parallel并行
    .peek(new Consumer<Integer>() {
    
     // peek调试用的
        @Override
        public void accept(Integer num) {
    
    
            System.out.println(num +Thread.currentThread().getName());//查看是否多线程执行
        }
    })
    .filter(num -> num > 5)
    .reduce((result, element) -> result + element)
    .get();
System.out.println(integer2);

You can also use the parallelStream method to directly get the parallel stream object

List<Author> authors = getAuthors();
authors.parallelStream()	//直接变成并行流

Guess you like

Origin blog.csdn.net/m0_51315555/article/details/127913909