Java8——Lambda、stream API
创始人
2024-02-12 10:04:03
0

Java8—Lambda、stream API

  • 一、Lambda表达式
    • 前提准备
    • 1. 语法
      • 1.1 无参数 无返回值
      • 1.2 一个参数 无返回值
      • 1.3 多个参数 多条语句 有返回值
    • 2. 函数式接口
      • 2.1 函数式接口
      • 2.2 内置的四大函数式接口
        • 2.2.1 Consumer
        • 2.2.2 Supplier
        • 2.2.3 Function
        • 2.2.4 Predicate
    • 3. 方法与构造器引用
      • 3.1 方法引用
        • 3.1.1 对象::方法名
        • 3.1.2 类::静态方法名
        • 3.1.3 类::方法名
      • 3.2 构造器引用
      • 3.3 数组引用
  • 二、stream API
    • 1. 创建
    • 2. 中间与终止操作
      • 2.1 筛选与切片
      • 2.2 映射
      • 2.3 排序
      • 2.4 查找与匹配
      • 2.5 归约与收集

一、Lambda表达式

前提准备

Employee类为接下来测试Lambda表达式和stream API提供数据支持。

public class Employee {private long eid;//员工IDprivate String name;//员工姓名private String department;//员工住址private double salary;//员工薪水private int age;//员工年龄public static List 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 employeesList = new ArrayList<>();employeesList.add(e1);employeesList.add(e2);employeesList.add(e3);employeesList.add(e4);employeesList.add(e5);return employeesList;}
}

1. 语法

(1) -> (2)左侧1是输入的参数列表,右侧2是需要执行的操作,又叫Lambda体。
Lambda表达式其实是在实现函数式接口中的抽象方法,是接口的一个实现类的对象,(1)对应抽象方法的参数列表,(2)对应方法实现时的具体操作。

1.1 无参数 无返回值

// () -> System.out.println(x)
Runnable runnable = () -> System.out.println("无参数无返回值");
runnable.run();

1.2 一个参数 无返回值

// (x)-> System.out.println(x), 此时(x)的()可以省略
Consumer consumer = (x) -> System.out.println(x);
consumer.accept(1);

1.3 多个参数 多条语句 有返回值

Comparator com = (x, y) -> {System.out.println("两个参数多条语句有返回值");return Integer.compare(x, y);
};
//只有一条语句时,{}和return可以省略
//Comparator com = (x, y) -> Integer.compare(x, y);

2. 函数式接口

2.1 函数式接口

接口中只有一个抽象方法的接口,用注解@FunctionalInterface修饰。

2.2 内置的四大函数式接口

Consumer: 消费型接口void accept(T t);Supplier: 供给型接口T get();Functional: 函数型接口R apply(T t);Predicate: 断言型接口boolean test(T t);

2.2.1 Consumer

//Consumer void accept(T t);
@Test
public void test1() {testConsumer(400, (c)-> System.out.println("消费 " + c + " 元"));//消费 400 元
}
public void testConsumer(int money, Consumer consumer){consumer.accept(money);
}

2.2.2 Supplier

//Supplier T get();
@Test
public void test2() {List list = testSupplier(5, () -> (int) (Math.random()*10));list.forEach(System.out::print);//84879
}
//返回num个随机整数
public List testSupplier(int num, Supplier supplier){ArrayList list = new ArrayList<>();for(int i=0; iInteger integer = supplier.get();list.add(integer);}return list;
}

2.2.3 Function

//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不包括4System.out.println(s2);//BCD
}
//字符串处理
public String testFunction(String str, Function fun){return fun.apply(str);
}

2.2.4 Predicate

//Predicate boolean test(T t);
@Test
public void test4() {ArrayList strList = new ArrayList<>();strList.add("a12");//length=3strList.add("b123");//4strList.add("c1234");//5strList.add("d12345");//6strList.add("e123456");//7List strings = testPredicate(strList, (str) -> str.length() < 5);System.out.println(strings);//[a12, b123]
}
//将满足条件的字符串存放到List中并返回
public List testPredicate(List strList, Predicate pre){ArrayList strings = new ArrayList<>();for (String s : strList) {if(pre.test(s)){strings.add(s);}}return strings;
}

3. 方法与构造器引用

3.1 方法引用

若Lambda体中的内容已经有方法实现了,则可以使用方法引用。
对象::方法名
类::静态方法名
类::方法名

注意:使用方法引用时,被引用的方法的参数列表和返回值类型要与函数式接口中待实现的方法的参数列表和返回值类型一致。

例如:Supplier { T get(); }接口中待实现的方法get()无参数有一个返回值,那么使用方法引用来实现get()方法时,被引用的方法也要无参数有一个返回值,如employee.getName(),此时就可以用getName()来作为get()的实现。

public static void main(String[] args) {//(str)->System.out.println(str)//输出方法已经实现了,不需要再书写Lambda表达式,使用方法引用Consumer con = System.out::println;con.accept("123");//123
}

3.1.1 对象::方法名

public static void main(String[] args) {Employee emp = Employee.getEmployeesList().get(1);//Supplier sup = () -> emp.getName();Supplier sup = emp::getName;//实现Supplier接口中get()方法(实现为emp.getName()),但是没有执行String name = sup.get();//执行getName()System.out.println(name);//老二
}

3.1.2 类::静态方法名

public static void main(String[] args) {//Comparator com = (x, y) -> Integer.compare(x, y);Comparator com = Integer::compare;//实现Comparator中的compare()静态方法,实现为Integer.compare()int res = com.compare(1, 2);//调用方法System.out.println(res);//-1(1比2小,返回-1)
}

3.1.3 类::方法名

(x, y) -> x.method(y),当第一个参数x是方法的调用者,第二个参数y是传入方法内的参数时,可以直接用类名调用实例方法,而不需要使用new对象来调用。

public static void main(String[] args) {//BiPredicate bp = (x, y) -> x.equals(y);BiPredicate bp = String::equals;boolean b = bp.test("abc", "abc");System.out.println(b);//true
}

3.2 构造器引用

类名::new:返回该类的一个对象

//Supplier sup = () -> new Employee();
//Supplier接口中的get()方法无参数,所以调用无参构造器
Supplier sup = Employee::new;
System.out.println(sup.get());
//Employee{eid=0, name='null', department='null', Salary=0.0, age=0}//Function一个参数,调用一个参数的构造器
Function fun = Employee::new; 
fun.apply("小明");//BiFunction两个参数,调用两个参数的构造器
Function biFun = Employee::new; 
fun.apply("小明", 22);

当然,使用构造器引用的前提是实体类中提供了相应的无参、有参构造器。

3.3 数组引用

数组类型[]::new:返回该类的一个数组

//Function funArr = (num) -> new String[num];
Function fun = String[]::new;
String[] funArr = fun.apply(5);
System.out.println(funArr.length);
//5

二、stream API

stream用于操作数据源(集合、数组等),是一种计算方式。

  • stream不存储数据;
  • stream不改变源数据;
  • stream中的计算操作只有在需要输出时才执行。

1. 创建

(1)集合对象.stream():集合流
(2)Arrays.stream(数组对象):数组流
(3)Stream.of()
parallelStream() 并行流先不学

//Collections集合对象直接调用stream()方法,创建集合流
List list = new ArrayList();
Stream stream1 = list.stream();//数组对象不能直接调用stream()方法,需要使用Arrays.stream(数组名),创建数组流
Employee[] emps = new Employee[10];
Stream stream2 = Arrays.stream(emps);//通过Stream类提供的静态方法of(),创建流
Stream stream3 = Stream.of(emps);

2. 中间与终止操作

2.1 筛选与切片

方法操作
filter(Predicate p)筛选出满足条件的数据
distinct()根据hashcode()和equals()去重,自定义实体类必须重写这两个方法,否则去重失败
limit(long n)保留前n个数据 [0, n)
skip(long n)跳过前n个数据,保留后max-n个数据
List emps = Employee.getEmployeesList();
Stream 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);

2.2 映射

方法操作
map(Function f)接收Lambda表达式,表达式规定的操作会在每个数据上都执行一次
flatMap()
List 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);
//老大 老二 老三 老四 老五

2.3 排序

方法操作
sorted()自然排序Comparable
sorted(Comparator com)定制排序Comparator
//1. sorted() 自然排序
List list = Arrays.asList("2aaa", "3bbb", "1ccc");
list.stream().sorted().forEach(System.out::println);//1ccc 2aaa 3bbb//2. sorted(Comparator com)按照年龄和姓名定制排序
List 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);

2.4 查找与匹配

方法操作
allMatch()匹配所有数据
anyMatch()至少匹配一个数据
noneMatch()都不匹配
findFirst()返回第一个数据
findAny()返回满足条件的数据中的任意一个
count()数据总数
max()最大值
min()最小值
sum()求和
List 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 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 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 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

2.5 归约与收集

方法操作
reduce()将数据按照指定规则重新组合,返回一个值
collect(Collector c)将数据组合成新的集合等,用于对数据进行汇总
Collectors.xCollector接口的实现类,Collectors中提供了一些静态方法

map()和reduce()经常一起使用,先使用map()将数据挑选出来,再使用reduce()进行处理。

//1. reduce(T identity, BinaryOperator accumulator) identity-起始值 accumulator-对元素的操作规则
List 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 emps = Employee.getEmployeesList();
Optional red2 = emps.stream().map(Employee::getSalary).reduce(Double::sum);
System.out.println(red2.get());//27500.0//3. collect(Collectors.toList/Set)  将所有员工姓名提取,并返回列表
List nameList = emps.stream().map(Employee::getName).collect(Collectors.toList());
System.out.println(nameList);
//[老大, 老二, 老三, 老四, 老五]//4. collect(Collectors.toCollection(Supplier sup)) 将数据收集到自定义集合中
LinkedList ll = emps.stream().map(Employee::getName).collect(Collectors.toCollection(LinkedList::new));//员工姓名存储到LinkedList中
System.out.println(ll);//5. collect(Collectors.toMap(keyMapper, valueMapper))
Map 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 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> 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>> 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);

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...