一、函数式接口
二、lambda表达式
package com.javabase.javabase.lambda;
import org.junit.Test;
import java.util.Comparator;
/**
* 1.举例: (o1,o2) -> Integer.compare(o1,o2);
* 2.格式:
* -> :lambda操作符 或 箭头操作符
* ->左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
* ->右边:lambda体 (其实就是重写的抽象方法的方法体)
* 3.本质:lambda 表达式的本质,作为一个函数式接口的实例(接口里面只有一个抽象方法)
* 4.函数式接口:如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口
* 5.lambda表达式的写法总结:
* ->左边:lambda形参列表的参数类型可以省略(类型推断);
* 如果lambda形参列表只一个参数,其一对()也可以省略,
* 如果没有入参,则是一对空()
* ->右边:lambda体应该使用一对{}包裹;
* 如果lambda体只一条执行语句(可能是return语句,省略这一对{}和return关键字,
* lambda有多条语句时,不可以省略省略这一对{}和return关键字
*/
public class LambdaTest {
/**
* @Author ShawnYang
* @Date 2019-11-04 10:32
* @Description thread 的lambda 表达式
* 修改人:
* 修改时间:
* 修改备注:
*/
public void test01(){
// 创建了一个实现runnable接口的匿名类,把这个类作为实参赋值给new Thread(),在小括号内实现匿名类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("shawnyang,you must working handly");
}
}).run();
}
public void test011(){
new Thread(() -> {
System.out.println("shawnyang,you must working handly");
}).run();
}
public void test02(){
// 创建一个继承Thread类的匿名类并重写了run方法,注意这上面的实现方式的区别,在大括号内实现匿名类
new Thread(){
@Override
public void run() {
super.run();
}
}.run();
}
/**
* @Author ShawnYang
* @Date 2019/11/4 0004 10:56
* @Description comparable 的lambda表达式
* 修改人:
* 修改时间:
* 修改备注:
* 实现注意:
*/
@Test
public void test03(){
int i = new Comparable<Integer>() {
@Override
public int compareTo(Integer o) {
return 0;
}
}.compareTo(3);
System.out.println(i);
}
@Test
public void test031(){
int i = ((Comparable<Integer>) o -> 0).compareTo(3);
System.out.println(i);
}
@Test
public void test04(){
int compare = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
}.compare(1, 2);
System.out.println(compare);
}
@Test
public void test041(){
int compare = ((Comparator<Integer>) (o1, o2) -> {
System.out.println("多条语句的执行情况的lambda语句");
return Integer.compare(o1, o2);
}).compare(1, 2);
System.out.println(compare);
}
// 常规引用简化为lambda表达式
@Test
public void test042(){
int compare = ((Comparator<Integer>) (o1, o2) -> Integer.compare(o1, o2)).compare(1, 2);
System.out.println(compare);
}
// lambda表达式简化为方法引用
@Test
public void test043(){
int compare = ((Comparator<Integer>) Integer::compare).compare(1, 2);
System.out.println(compare);
}
}
三、方法引用
package com.javabase.javabase.methodReferences;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 方法引用的使用
*
* 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
*
* 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以
* 方法引用,也是函数式接口的实例。
*
* 3. 使用格式: 类(或对象) :: 方法名
*
* 4. 具体分为如下的三种情况:
* 情况1 对象 :: 非静态方法
* 情况2 类 :: 静态方法
*
* 情况3 类 :: 非静态方法
*
* 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的
* 形参列表和返回值类型相同!(针对于情况1和情况2)
*
* Created by shkstart.
*/
public class MethodRefTest {
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
//传递给下面lambda表达式方法体的操作System.out.println(str) 已经有实现的方法,此时可以把lambda表达式改造为方法引用
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("北京");
System.out.println("*******************");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("beijing");
}
//Supplier中的T get()
//Employee中的String getName()
// 入参类型和返回值类型相同
@Test
public void test2() {
Employee emp = new Employee(1001,"Tom",23,5600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));
System.out.println("*******************");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12,3));
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
Function<Double,Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
System.out.println("*******************");
Function<Double,Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
System.out.println("*******************");
Function<Double,Long> func2 = Math::round;
System.out.println(func2.apply(12.6));
}
// 情况三:类 :: 实例方法 (有难度)
// Comparator中的int comapre(T t1,T t2),第一个参数作为调用者的身份出现的
// String中的int t1.compareTo(t2)
@Test
public void test5() {
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
System.out.println("*******************");
Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("abd","abm"));
}
//BiPredicate中的boolean test(T t1, T t2);第一个参数作为调用者的身份出现的
//String中的boolean t1.equals(t2)
@Test
public void test6() {
BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
System.out.println(pre1.test("abc","abc"));
System.out.println("*******************");
BiPredicate<String,String> pre2 = String :: equals;
System.out.println(pre2.test("abc","abd"));
}
// Function中的R apply(T t),第一个参数作为调用者的身份出现的,所以下面的方法入参是空
// Employee中的String getName();
@Test
public void test7() {
Employee employee = new Employee(1001, "Jerry", 23, 6000);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee));
System.out.println("*******************");
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
}
四、构造器引用和数组引用
package com.javabase.javabase.methodReferences;
import org.junit.Test;
import java.util.Arrays;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 一、构造器引用
* 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
* 抽象方法的返回值类型即为构造器所属的类的类型
*
* 二、数组引用
* 大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。
*
* Created by shkstart
*/
public class ConstructorRefTest {
//构造器引用
//Supplier中的T get()
//Employee的空参构造器:Employee()
@Test
public void test1(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("*******************");
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<Employee> sup2 = Employee :: new;
System.out.println(sup2.get());
}
//Function中的R apply(T t)
@Test
public void test2(){
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
System.out.println("*******************");
Function<Integer,Employee> func2 = Employee :: new;
Employee employee1 = func2.apply(1002);
System.out.println(employee1);
}
//BiFunction中的R apply(T t,U u)
@Test
public void test3(){
BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
System.out.println(func1.apply(1001,"Tom"));
System.out.println("*******************");
BiFunction<Integer,String,Employee> func2 = Employee :: new;
System.out.println(func2.apply(1002,"Tom"));
}
//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("*******************");
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
}
五、stream 接口
1 stream的创建
package com.javabase.javabase.streamAPI;
import com.javabase.javabase.methodReferences.Employee;
import com.javabase.javabase.methodReferences.EmployeeData;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @Author ShawnYang
* @Date 2019-11-04 14:34
* @Description TODO
* 修改人:
* 修改时间:
* 修改备注:
*
* 1.Stream API的理解:
* 1.1 Stream关注的是对数据的运算,与CPU打交道
* 集合关注的是数据的存储,与内存打交道
*
* 1.2 java8提供了一套api,使用这套api可以对内存中的数据进行过滤、排序、映射、归约等操作。类似于sql对数据库中表的相关操作。
*
*
* 2.注意点:
* * ①Stream 自己不会存储元素。
* * ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
* * ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
*
* 3.Stream的使用流程:
* * ① Stream的实例化
* * ② 一系列的中间操作(过滤、映射、...)
* * ③ 终止操作
*
* 4.使用流程的注意点:
* * 4.1 一个中间操作链,对数据源的数据进行处理
* * 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
*
*/
public class StreamAPI {
//创建 Stream方式一:通过集合
public void test01(){
List<Employee> employees = EmployeeData.getEmployees();
//default Stream<E> stream() : 返回一个顺序流,按照顺序获取集合里面的元素
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream() : 返回一个并行流,同时取出集合里面的元素,顺序可能会有变化
Stream<Employee> employeeStream = employees.parallelStream();
}
//创建 Stream方式二:通过数组
public void test02(){
int[] arr={1,2,3,4,5,6};
//调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001,"Tom");
Employee e2 = new Employee(1002,"Jerry");
Employee[] arr1 = new Employee[]{e1,e2};
Stream<Employee> stream1 = Arrays.stream(arr1);
}
//创建 Stream方式三:通过Stream的of()
public void test03(){
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
}
//创建 Stream方式四:创建无限流(一般需要造数据的情况下使用)
@Test
public void test04(){
//迭代
//public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
//遍历前10个偶数
Stream.iterate(0,t->t+2 ).limit(10).forEach(System.out::println);
//生成
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
2 stream 的中间操作
package com.javabase.javabase.streamAPI;
import com.javabase.javabase.methodReferences.Employee;
import com.javabase.javabase.methodReferences.EmployeeData;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* @Author ShawnYang
* @Date 2019-11-04 15:31
* @Description stream的中间操作
* 修改人:
* 修改时间:
* 修改备注:
*/
public class StreamAPI1 {
@Test
public void test01(){
// 1 中间操作--筛选与切片
List<Employee> employees = EmployeeData.getEmployees();
Stream<Employee> stream = employees.stream();
//联系查询员工表中的信息大于700的员工
stream.filter(employee -> employee.getSalary()>7000).forEach(System.out::println);
// 2 中间操作--截断,使其元素不超过给定数量 stream流只能使用一次,上面的过滤已经使用过了,所有下面的employees.stream() 表示重新形成一个
//一个新的流
System.out.println();
employees.stream().limit(3).forEach(System.out::println);
// 3 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流
System.out.println();
employees.stream().skip(3).forEach(System.out::println);
// 4 distinct() 通过流所生成的元素的hashcode和equal值进行去重
System.out.println();
employees.add(new Employee(1010,"刘强东",40,8000));
employees.add(new Employee(1010,"刘强东",40,8000));
employees.add(new Employee(1010,"刘强东",40,8000));
employees.add(new Employee(1010,"刘强东",40,8000));
employees.stream().distinct().forEach(System.out::println);
}
@Test
public void test02(){
//5 Map映射(接受一个函数作为参数,将元素装换成其他形式或者提取信息,该函数会应用到每个元素上),类似于list1.add(list2),把被添加的list当成整体
// list 里面的值映射大写
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream().map(str ->str.toUpperCase() ).forEach(System.out::println);
//5.2 映射联系练习:获取员工姓名长度大于3的员工的姓名
List<Employee> employees = EmployeeData.getEmployees();
Stream<Employee> stream = employees.stream();
Stream<String> stringStream = stream.map(Employee::getName);
stringStream.filter(str->str.length()>3).forEach(System.out::println);
//5.2 映射联系练习:stream 里面存在stream(多层嵌套),可以使用flatMap 进行简化
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPI1::fromStringToStream);
// 下面的s实际上表示的stream对象
streamStream.forEach(s->{
s.forEach(System.out::println);
});
//6 flatMap 接受一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流都连成一个流,list1.addAll(list2) 把被添加的list中的元素一个个取出再添加进去
list.stream().flatMap(StreamAPI1::fromStringToStream).forEach(System.out::println);
}
// 将字符串中的多个字符构成的集合转换为对应的Stream实例
public static Stream<Character> fromStringToStream(String str){
ArrayList<Character> characters = new ArrayList<>();
for (Character c : str.toCharArray()) {
characters.add(c);
}
return characters.stream();
}
@Test
public void test03(){
// 6 排序: sorted()--自然排序,由小到大
List<Integer> integers = Arrays.asList(11, 12, 15, 13, 8, 50);
integers.stream().sorted().forEach(System.out::println);
// 下面抛异常,原因是Employee没有实现Comparable接口
List<Employee> employees = EmployeeData.getEmployees();
// employees.stream().sorted().forEach(System.out::println);
// 6 排序:sorted(Comparator com)--定制排序
employees.stream().sorted((i1,i2)->Integer.compare(i1.getAge(), i2.getAge())).forEach(System.out::println);
}
}
三、stream的终止操作
package com.javabase.javabase.streamAPI;
import com.javabase.javabase.methodReferences.Employee;
import com.javabase.javabase.methodReferences.EmployeeData;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @Author ShawnYang
* @Date 2019-11-04 17:06
* @Description stream的终止操作
* 修改人:
* 修改时间:
* 修改备注:
*/
public class StreamAPI2 {
//查找与匹配以及遍历
@Test
public void test01(){
List<Employee> employees = EmployeeData.getEmployees();
//allMatch 全部需要满足
boolean b = employees.stream().allMatch(employee -> employee.getAge() > 18);
System.out.println(b);
//anyMatch 只要有一个满足就行
boolean b1 = employees.stream().anyMatch(employee -> employee.getSalary() > 10000);
System.out.println(b1);
//nonmatch 是否存在不匹配的元素
boolean b2 = employees.stream().noneMatch(employee -> employee.getSalary() > 2000);
System.out.println(b2);
//findFirst
Optional<Employee> first = employees.stream().findFirst();
System.out.println(first);
//findAny
Optional<Employee> any = employees.stream().findAny();
Optional<Employee> any1 = employees.parallelStream().findAny();
System.out.println(any);
System.out.println(any1);
}
@Test
public void test02(){
List<Employee> employees = EmployeeData.getEmployees();
//count()--返回流元素中满足条件的总个数
long count = employees.stream().filter(employee -> employee.getSalary() > 5000).count();
System.out.println(count);
//max 返回最高的工资
Optional<Double> max = employees.stream().map(employee -> employee.getSalary()).max(Double::compareTo);
System.out.println(max);
//min 返回最低工资的员工
// employees.stream().map(employee -> employee.getSalary()).min(Double::compareTo).map(d->)
Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(min);
// forEach 遍历--内部迭代
employees.stream().forEach(System.out::println);
// forEach 集合的自身迭代
employees.forEach(System.out::println);
}
//归约reduce 可将流中的元素反复结合起来,得到一个值
@Test
public void test03(){
// 计算1-10 的自然数求和
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 0 是初始值,reduce 可将流中的元素反复结合起来,得到一个值
Integer reduce = integers.stream().reduce(0, Integer::sum);
System.out.println(reduce);
//计算公司所有员工工资总和
List<Employee> employees = EmployeeData.getEmployees();
// 两种写法都行
Optional<Double> reduce1 = employees.stream().map(employee -> employee.getSalary()).reduce(Double::sum);
Optional<Double> reduce2 = employees.stream().map(Employee::getSalary).reduce((d1, d2) -> d1 + d2);
System.out.println(reduce1);
System.out.println(reduce2);
}
//收集 collect(Collector c) 接口中方法的实现决定了如果对流执行收集的操作(如收集到list、set、map) 将流转换为其他形式
// Collector使用类提供了很多静态方法,可以方便的创建常见的收集器实例
@Test
public void test04(){
//查找工资大于6000的员工,结果返回一个list或set
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> list = employees.stream().filter(employee -> employee.getSalary() > 6000).collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println();
Set<Employee> set = employees.stream().filter(employee -> employee.getSalary() > 6000).collect(Collectors.toSet());
set.forEach(System.out::println);
}
}