Employee类为接下来测试Lambda表达式和stream API提供数据支持。
public class Employee {
private long eid;//员工ID
private String name;//员工姓名
private String department;//员工住址
private double salary;//员工薪水
private int age;//员工年龄
public static List<Employee> getEmployeesList(){
Employee e1 = new Employee(1001, "老大", "1-101", 4500, 23);
Employee e2 = new Employee(1002, "老二", "1-102", 5000, 24);
Employee e3 = new Employee(1003, "老三", "1-103", 5500, 25);
Employee e4 = new Employee(1004, "老四", "1-104", 6000, 26);
Employee e5 = new Employee(1005, "老五", "1-105", 6500, 27);
ArrayList<Employee> employeesList = new ArrayList<>();
employeesList.add(e1);
employeesList.add(e2);
employeesList.add(e3);
employeesList.add(e4);
employeesList.add(e5);
return employeesList;
}
}
(1) -> (2)
左侧1是输入的参数列表,右侧2是需要执行的操作,又叫Lambda体。
Lambda表达式其实是在实现函数式接口中的抽象方法,是接口的一个实现类的对象,(1)对应抽象方法的参数列表,(2)对应方法实现时的具体操作。
// () -> System.out.println(x)
Runnable runnable = () -> System.out.println("无参数无返回值");
runnable.run();
// (x)-> System.out.println(x), 此时(x)的()可以省略
Consumer<Integer> consumer = (x) -> System.out.println(x);
consumer.accept(1);
Comparator<Integer> com = (x, y) -> {
System.out.println("两个参数多条语句有返回值");
return Integer.compare(x, y);
};
//只有一条语句时,{}和return可以省略
//Comparator com = (x, y) -> Integer.compare(x, y);
接口中只有一个抽象方法的接口,用注解@FunctionalInterface
修饰。
Consumer<T>: 消费型接口
void accept(T t);
Supplier<T>: 供给型接口
T get();
Functional<T, R>: 函数型接口
R apply(T t);
Predicate<T>: 断言型接口
boolean test(T t);
//Consumer void accept(T t);
@Test
public void test1() {
testConsumer(400, (c)-> System.out.println("消费 " + c + " 元"));
//消费 400 元
}
public void testConsumer(int money, Consumer<Integer> consumer){
consumer.accept(money);
}
//Supplier T get();
@Test
public void test2() {
List<Integer> list = testSupplier(5, () -> (int) (Math.random()*10));
list.forEach(System.out::print);
//84879
}
//返回num个随机整数
public List<Integer> testSupplier(int num, Supplier<Integer> supplier){
ArrayList<Integer> list = new ArrayList<>();
for(int i=0; i<num; i++){
Integer integer = supplier.get();
list.add(integer);
}
return list;
}
//Functional R apply(T t);
@Test
public void test3() {
//字符串变大写
String s1 = testFunction("aBcdEfg", (str) -> str.toUpperCase());
System.out.println(s1);
//ABCDEFG
//截取子串
String s2 = testFunction(s1, (str) -> str.substring(1, 4));//包括1不包括4
System.out.println(s2);
//BCD
}
//字符串处理
public String testFunction(String str, Function<String, String> fun){
return fun.apply(str);
}
//Predicate boolean test(T t);
@Test
public void test4() {
ArrayList<String> strList = new ArrayList<>();
strList.add("a12");//length=3
strList.add("b123");//4
strList.add("c1234");//5
strList.add("d12345");//6
strList.add("e123456");//7
List<String> strings = testPredicate(strList, (str) -> str.length() < 5);
System.out.println(strings);
//[a12, b123]
}
//将满足条件的字符串存放到List中并返回
public List<String> testPredicate(List<String> strList, Predicate<String> pre){
ArrayList<String> strings = new ArrayList<>();
for (String s : strList) {
if(pre.test(s)){
strings.add(s);
}
}
return strings;
}
若Lambda体中的内容已经有方法实现了,则可以使用方法引用。
对象::方法名
类::静态方法名
类::方法名
注意:使用方法引用时,被引用的方法的参数列表和返回值类型要与函数式接口中待实现的方法的参数列表和返回值类型一致。
例如:Supplier
接口中待实现的方法get()无参数有一个返回值,那么使用方法引用来实现get()方法时,被引用的方法也要无参数有一个返回值,如employee.getName()
,此时就可以用getName()来作为get()的实现。
public static void main(String[] args) {
//(str)->System.out.println(str)
//输出方法已经实现了,不需要再书写Lambda表达式,使用方法引用
Consumer<String> con = System.out::println;
con.accept("123");
//123
}
public static void main(String[] args) {
Employee emp = Employee.getEmployeesList().get(1);
//Supplier sup = () -> emp.getName();
Supplier<String> sup = emp::getName;//实现Supplier接口中get()方法(实现为emp.getName()),但是没有执行
String name = sup.get();//执行getName()
System.out.println(name);
//老二
}
public static void main(String[] args) {
//Comparator com = (x, y) -> Integer.compare(x, y);
Comparator<Integer> com = Integer::compare;//实现Comparator中的compare()静态方法,实现为Integer.compare()
int res = com.compare(1, 2);//调用方法
System.out.println(res);//-1(1比2小,返回-1)
}
(x, y) -> x.method(y)
,当第一个参数x是方法的调用者,第二个参数y是传入方法内的参数时,可以直接用类名调用实例方法,而不需要使用new对象来调用。
public static void main(String[] args) {
//BiPredicate bp = (x, y) -> x.equals(y);
BiPredicate<String, String> bp = String::equals;
boolean b = bp.test("abc", "abc");
System.out.println(b);
//true
}
类名::new
:返回该类的一个对象
//Supplier sup = () -> new Employee();
//Supplier接口中的get()方法无参数,所以调用无参构造器
Supplier<Employee> sup = Employee::new;
System.out.println(sup.get());
//Employee{eid=0, name='null', department='null', Salary=0.0, age=0}
//Function一个参数,调用一个参数的构造器
Function<String, Employee> fun = Employee::new;
fun.apply("小明");
//BiFunction两个参数,调用两个参数的构造器
Function<String, Integer, Employee> biFun = Employee::new;
fun.apply("小明", 22);
当然,使用构造器引用的前提是实体类中提供了相应的无参、有参构造器。
数组类型[]::new
:返回该类的一个数组
//Function funArr = (num) -> new String[num];
Function<Integer, String[]> fun = String[]::new;
String[] funArr = fun.apply(5);
System.out.println(funArr.length);
//5
stream用于操作数据源(集合、数组等),是一种计算方式。
(1)集合对象.stream()
:集合流
(2)Arrays.stream(数组对象)
:数组流
(3)Stream.of()
parallelStream() 并行流先不学
//Collections集合对象直接调用stream()方法,创建集合流
List<String> list = new ArrayList<String>();
Stream<String> stream1 = list.stream();
//数组对象不能直接调用stream()方法,需要使用Arrays.stream(数组名),创建数组流
Employee[] emps = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(emps);
//通过Stream类提供的静态方法of(),创建流
Stream<Employee> stream3 = Stream.of(emps);
方法 | 操作 |
---|---|
filter(Predicate p) | 筛选出满足条件的数据 |
distinct() | 根据hashcode()和equals()去重,自定义实体类必须重写这两个方法,否则去重失败 |
limit(long n) | 保留前n个数据 [0, n) |
skip(long n) | 跳过前n个数据,保留后max-n个数据 |
List<Employee> emps = Employee.getEmployeesList();
Stream<Employee> stream = emps.stream();
//1. filter(Predicate p) 筛选年龄大于24的员工
stream.filter((e)->e.getAge()>24)
.forEach(System.out::println);
//Employee{eid=1003, name='老三', department='1-103', Salary=5500.0, age=25}
//Employee{eid=1004, name='老四', department='1-104', Salary=6000.0, age=26}
//Employee{eid=1005, name='老五', department='1-105', Salary=6500.0, age=27}
//2. limit(long n) 保留年龄大于24的员工的前两位
stream.filter((e)->e.getAge()>24)
.limit(2)
.forEach(System.out::println);
//Employee{eid=1003, name='老三', department='1-103', Salary=5500.0, age=25}
//Employee{eid=1004, name='老四', department='1-104', Salary=6000.0, age=26}
//3. skip(long n) 跳过年龄大于24的员工的前两位
stream.filter((e)->e.getAge()>24)
.skip(2)
.forEach(System.out::println);
//Employee{eid=1005, name='老五', department='1-105', Salary=6500.0, age=27}
//4.distinct() 根据hashcode()和equals()去重
stream.filter((e)->e.getAge()>24)
.distinct()
.forEach(System.out::println);
方法 | 操作 |
---|---|
map(Function f) | 接收Lambda表达式,表达式规定的操作会在每个数据上都执行一次 |
flatMap() |
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
//1. map(Function f) 下例中String::toUpperCase操作会在每个数据上执行一次
list.stream().map(String::toUpperCase)
.forEach(System.out::println);
//AAA BBB CCC
//例2:获取全体员工姓名
Employee.getEmployeesList().stream()
.map(Employee::getName)
.forEach(System.out::println);
//老大 老二 老三 老四 老五
方法 | 操作 |
---|---|
sorted() | 自然排序Comparable |
sorted(Comparator com) | 定制排序Comparator |
//1. sorted() 自然排序
List<String> list = Arrays.asList("2aaa", "3bbb", "1ccc");
list.stream().sorted().forEach(System.out::println);//1ccc 2aaa 3bbb
//2. sorted(Comparator com)按照年龄和姓名定制排序
List<Employee> emps = Employee.getEmployeesList();
emps.stream().sorted((e1, e2) -> {
if(e1.getAge()==e2.getAge()){
return e1.getName().compareTo(e2.getName());
}else{
return Math.min(e1.getAge(), e2.getAge());
}
}).forEach(System.out::println);
方法 | 操作 |
---|---|
allMatch() | 匹配所有数据 |
anyMatch() | 至少匹配一个数据 |
noneMatch() | 都不匹配 |
findFirst() | 返回第一个数据 |
findAny() | 返回满足条件的数据中的任意一个 |
count() | 数据总数 |
max() | 最大值 |
min() | 最小值 |
sum() | 求和 |
List<Employee> emps = Employee.getEmployeesList();
//1. allMatch() 所有员工年龄都大于22吗?
boolean b1 = emps.stream().allMatch((e)->e.getAge() > 22);//true
//2. anyMatch() 所有员工中有住在1-103的吗?
boolean b2 = emps.stream().anyMatch((e) -> e.getDepartment().equals("1-103"));//true
//3. noneMatch() 所有员工没有工资大于1w的?
boolean b3 = emps.stream().noneMatch((e) -> e.getSalary() > 10000);//true
//4. findFirst() 找到员工工资大于5000中的第一个人
Optional<Employee> first = emps.stream()
.filter((e) -> e.getSalary() >= 5000)
.findFirst();//Optional-防止空指针的容器类
System.out.println(first.get());
//Employee{eid=1002, name='老二', department='1-102', Salary=5000.0, age=24}
//5. findAny() 找到员工年龄大于24中的任意一个
Optional<Employee> any = emps.stream()
.filter((e) -> e.getAge() > 24)
.findAny();
System.out.println(any);
//Optional[Employee{eid=1003, name='老三', department='1-103', Salary=5500.0, age=25}]
//6. count() 计算年龄大于24的员工总数
long count = emps.stream()
.filter((e) -> e.getAge() > 24)
.count();
System.out.println(count);//3
//7. max()/min() 找到员工中年龄工资最多/最少的
Optional<Employee> max = emps.stream().max(Comparator.comparingDouble(Employee::getSalary));
System.out.println(max.get());
//Employee{eid=1005, name='老五', department='1-105', Salary=6500.0, age=27}
//8. sum() 计算员工工资总和
double sum = emps.stream()
.mapToDouble(Employee::getSalary)
.sum();//27500.0
方法 | 操作 |
---|---|
reduce() | 将数据按照指定规则重新组合,返回一个值 |
collect(Collector c) | 将数据组合成新的集合等,用于对数据进行汇总 |
Collectors.x | Collector接口的实现类,Collectors中提供了一些静态方法 |
map()和reduce()经常一起使用,先使用map()将数据挑选出来,再使用reduce()进行处理。
//1. reduce(T identity, BinaryOperator accumulator) identity-起始值 accumulator-对元素的操作规则
List<Integer> list = Arrays.asList(1, 2, 3, 4 ,5, 6, 7, 8, 9, 10);
Integer red1 = list.stream().reduce(0, Integer::sum);
System.out.println(red1);//55
//2. reduce() 计算员工工资的总和
List<Employee> emps = Employee.getEmployeesList();
Optional<Double> red2 = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println(red2.get());//27500.0
//3. collect(Collectors.toList/Set) 将所有员工姓名提取,并返回列表
List<String> nameList = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
System.out.println(nameList);
//[老大, 老二, 老三, 老四, 老五]
//4. collect(Collectors.toCollection(Supplier sup)) 将数据收集到自定义集合中
LinkedList<String> ll = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(LinkedList::new));//员工姓名存储到LinkedList中
System.out.println(ll);
//5. collect(Collectors.toMap(keyMapper, valueMapper))
Map<String, String> collMap = emps.stream()
.collect(Collectors.toMap(Employee::getName, Employee::getDepartment));
System.out.println(collMap);
//{老二=1-102, 老四=1-104, 老三=1-103, 老大=1-101, 老五=1-105}
方法 | 操作 |
---|---|
Collectors.counting() | 总数 |
Collectors.averaging() | 平均值 |
Collectors.summing() | 求和 |
Collectors.maxBy(Comparator c) | 取最大值(minBy取最小值) |
Collectors.groupingBy(Function f) | 分组,返回map |
Collectors.join() | 连接 |
List<Employee> emps = Employee.getEmployeesList();
//1. Collectors.counting()
Long c1 = emps.stream().collect(Collectors.counting());//5
//2. Collectors.averaging()
Double c2 = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));//5500
//3. Collectors.summing()
//emps.stream().mapToDouble(Employee::getSalary).sum();
Double c3 = emps.stream().
collect(Collectors.summingDouble(Employee::getSalary));//27500.0
//4. Collectors.groupBy()
Map<Integer, List<Employee>> group = emps.stream().collect(Collectors.groupingBy(Employee::getAge));
System.out.println(group);
//{23=[Employee{eid=1001, name='老大', department='1-101', Salary=4500.0, age=23}],
// 24=[Employee{eid=1002, name='老二', department='1-102', Salary=5000.0, age=24}],
// 25=[Employee{eid=1003, name='老三', department='1-103', Salary=5500.0, age=25}],
// 26=[Employee{eid=1004, name='老四', department='1-104', Salary=6000.0, age=26}],
// 27=[Employee{eid=1005, name='老五', department='1-105', Salary=6500.0, age=27}]}
//5. Collectors.groupBy(Collectors.groupBy()) 多级分组:按照多个属性分组
Map<Integer, Map<String, List<Employee>>> multGroup = emps.stream()
.collect(Collectors.groupingBy(Employee::getAge, Collectors.groupingBy(Employee::getDepartment)));
System.out.println(multGroup);//为了测试,数据已修改
//{23={1-101=[Employee{eid=1001, name='老大', department='1-101', Salary=4500.0, age=23}]},
// 24={1-102=[Employee{eid=1002, name='老二', department='1-102', Salary=5000.0, age=24},
// Employee{eid=1003, name='老三', department='1-102', Salary=5500.0, age=24}]},
// 26={1-105=[Employee{eid=1005, name='老五', department='1-105', Salary=6500.0, age=26}],
// 1-104=[Employee{eid=1004, name='老四', department='1-104', Salary=6000.0, age=26}]}}
//6. Collectors.join()
String joinName = emps.stream().map(Employee::getName).collect(Collectors.joining());
System.out.println(joinName);//老大老二老三老四老五
Employee e1 = new Employee(1001, "老大", "1-101", 4500, 23);
Employee e2 = new Employee(1002, "老二", "1-102", 5000, 24);
Employee e3 = new Employee(1003, "老三", "1-102", 5500, 24);
Employee e4 = new Employee(1004, "老四", "1-104", 6000, 26);
Employee e5 = new Employee(1005, "老五", "1-105", 6500, 26);