Java8——Lambda、stream API

一、Lambda表达式

前提准备

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. 语法

(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<Integer> consumer = (x) -> System.out.println(x);
consumer.accept(1);

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

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

2. 函数式接口

2.1 函数式接口

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

2.2 内置的四大函数式接口

Consumer<T>: 消费型接口
	void accept(T t);

Supplier<T>: 供给型接口
	T get();

Functional<T, R>: 函数型接口
	R apply(T t);

Predicate<T>: 断言型接口
	boolean test(T t);
2.2.1 Consumer
//Consumer<T> 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);
}
2.2.2 Supplier
//Supplier<T> 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;
}
2.2.3 Function
//Functional<T, R> 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);
}
2.2.4 Predicate
//Predicate<T> 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;
}

3. 方法与构造器引用

3.1 方法引用

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

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

例如:Supplier<T> { T get(); }接口中待实现的方法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
}
3.1.1 对象::方法名
public static void main(String[] args) {
    Employee emp = Employee.getEmployeesList().get(1);
    //Supplier<String> sup = () -> emp.getName();
    Supplier<String> 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<Integer> 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)
}
3.1.3 类::方法名

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

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

3.2 构造器引用

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

//Supplier<Employee> 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);

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

3.3 数组引用

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

//Function<Integer, String[]> funArr = (num) -> new String[num];
Function<Integer, String[]> 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<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);

2. 中间与终止操作

2.1 筛选与切片

方法操作
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);

2.2 映射

方法操作
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);
//老大 老二 老三 老四 老五

2.3 排序

方法操作
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);

2.4 查找与匹配

方法操作
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

2.5 归约与收集

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

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

//1. reduce(T identity, BinaryOperator<T> 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);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值