Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])

文章目录

  • ✨Java 8 的新特性
    • 1、Lambda表达式
      • 优化方式一
      • 优化方式二
      • 优化方式三
      • 优化方式四
    • 2、Lambda基础语法
      • 语法格式一
      • 语法格式二
      • 语法格式三
      • 语法格式四
      • 语法格式五
      • 语法格式六
    • 3、函数式接口
    • 4、Java8中司法核心函数式接口
      • 消费性接口
      • 供给型接口
      • 函数型接口
      • 断言型接口
      • 子接口
    • 5、方法引用
      • 对象::实例方法名
        • 例1
        • 例2
      • 类::静态方法名
      • 类::实例方法名
    • 6、构造器引用
    • 7、Stream API
      • 创建流
      • 中间操作
        • filter
        • limit
        • skip
        • distinct
      • 映射
        • map
        • flatmap
      • 排序
    • 8、Stream API 终止操作
        • allMatch
        • anyMatch
        • noneMatch
        • findFirst
        • findAny
        • count
        • max
        • min
        • reduce
        • collect
          • collect分组
          • collect多级分组
          • collect分区
          • collect-summarizingDouble返回平均值最大值最小值.....
          • collect-join

✨Java 8 的新特性

  • 速度更快 (对于底层的数据结构有了改变、对底层的内存结构也有了改变【将堆中的方法区删除,取而代之的是MetaSpace元空间,使用了物理内存,当垃圾回收机制回收的概率变低了,因为是物理内存变大了】)

  • 代码更少(增加了新的语法Lambda表达式)

  • 强大的Stream API

  • 便于并行

  • 最大化的减少空指针异常

1、Lambda表达式

需求1:获取当前公司中员工年龄大于35的员工信息

​ 数据准备:

​ Employee类:

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-10-26 10:04
 */

public class Employee {
    private String name;
    private int age;
    private double salary;

    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public Employee() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

​ employees-list:

List<Employee> employees = Arrays.asList(
                new Employee("张三",18,9999.99),
                new Employee("李四",38,5555.55),
                new Employee("王五",50,6666.66),
                new Employee("赵六",16,3333.33),
                new Employee("田七",8,7777.77)
        );

方法一进行实现:

public List<Employee> filterEmployees(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : employees) {
            if (emp.getAge() >= 35) {
                emps.add(emp);
            }
        }
        return emps;
    }

代码截图:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第1张图片

现在需求改变了,变为:获取当前公司中员工工资大于5000的员工信息,那么现在意味着需要又要多写一个一模一样的方法:

public List<Employee> filterEmployees2(List<Employee> list) {
        List<Employee> emps = new ArrayList<>();
        for (Employee emp : employees) {
            if (emp.getSalary() >= 5000) {
                emps.add(emp);
            }
        }
        return emps;
    }

​ 唯独只变了一句话,这样就增加了代码的冗余程度了,所以设计模式就是对代码最好的优化方式;

优化方式一

创建一个MyPredicate接口:

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-10-26 10:25
 */

public interface MyPredicate<T> {
    public boolean test(T t);
}

创建个该接口的实现类:

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-10-26 10:25
 */

public class FilterEmployeeByAge implements MyPredicate<Employee> {
    @Override
    public boolean test(Employee t) {
        return t.getAge() >= 35;
    }
}

编写方法:

public List<Employee> filterEmployee(List<Employee> list,MyPredicate<Employee> emp){
    List<Employee> emps = new ArrayList<>();
    for (Employee employee : employees) {
        if (emp.test(employee)){
            emps.add(employee);
        }
    }
    return emps;
}

进行使用:

// 优化方式1:
@Test
public void test2(){
    List<Employee> emps = filterEmployee(this.employees, new FilterEmployeeByAge());
    for (Employee emp : emps) {
        System.out.println(emp);
    }
}

此时此刻,要是过滤需求改变了,需要按照工资过滤,那么现在设计好的东西就不需要动了,只需要再创建一个实现类,不需要改变filterEmployee方法:

/**
 * @version 1.0
 * @author: YxinMiracle
 * @date: 2021-10-26 10:33
 */

public class FilterEmployeeBySalary implements MyPredicate<Employee> {
    @Override
    public boolean test(Employee employee) {
        return employee.getSalary() >= 5000;
    }
}

运行使用:

@Test
public void test2(){
    List<Employee> emps = filterEmployee(this.employees, new FilterEmployeeByAge());
    for (Employee emp : emps) {
        System.out.println(emp);
    }
    System.out.println("-------------------------");

    List<Employee> emps2 = filterEmployee(this.employees, new FilterEmployeeBySalary());
    for (Employee emp : emps) {
        System.out.println(emp);
    }
}

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第2张图片

之前多一个需求就需要多一个方法,现在只需要写一个filterEmployee,在需求增加的时候就只需要多增加一个实现类即可,这种设计模式就称为策略设计模式

现在也有一个不好的方法,每次多一个策略就需要多一个实现类,这样是非常不方便的;

优化方式二

那么现在就有优化方式二,使用匿名内部类的方式:

@Test
public void test3(){
    List<Employee> employees = filterEmployee(this.employees, new MyPredicate<Employee>() {
        @Override
        public boolean test(Employee employee) {
            return employee.getSalary() <= 5000;
        }
    });
    for (Employee employee : employees) {
        System.out.println(employee);
    }
}

使用匿名内部类实现接口:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第3张图片

但是有用的只有一句,可读性降低了,还更加混乱了。

优化方式三

于是就出现了优化方式三,使用lambda表达式:

@Test
public void test4(){
    List<Employee> employees = filterEmployee(this.employees, (e) -> e.getSalary() <= 5000);
    employees.forEach(System.out::println);
}

就把最关键的代码提取出来了;

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第4张图片

优化方式四

当然了要是不满意的话还有优化方式四,就一行代码解决问题,这就是streamApi:

@Test
public void test5(){
    employees.stream().filter(e->e.getSalary()<=5000).forEach(System.out::println);
}

结果截图:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第5张图片

2、Lambda基础语法

Lambda表达式的基础语法:Java8中进入了一个新的操作符“->” 该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两部分

左侧:Lambda表达式的参数列表

右侧:Lambda表达式中所需要执行的功能,即Lambda体

但是lambda表达式,需要一个函数式接口的支持,就是指这个接口只有一个抽象方法

语法格式一

语法格式一:无参数,无返回值

() -> System.out.println(“xxxxx”)

exp1Runnable接口

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第6张图片

@Test
public void test6(){
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("hello world");
        }
    };

    System.out.println("------------------------------");

    Runnable r1 = () -> System.out.println("hello world");

    r.run();
    r1.run();
}

语法格式二

语法格式二:有一个参数,无返回值

exp:Consumer接口

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第7张图片

代码:

@Test
public void test7(){
    Consumer<String> con = (x) -> System.out.println(x); // 对accept这个抽象方法的实现
    con.accept("cyxnbnb");
}

结果截图:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第8张图片

语法格式三

语法格式三:只有一个参数,那么小括号可以不写

@Test
public void test8(){
    Consumer<String> con = x -> System.out.println(x);
    con.accept("cyxnbnb");
}

语法格式四

语法格式四:有两个以上的参数,并且lambda体中有多条语句,lambda方法体要用{}包起来

代码:

@Test
public void test9(){
    Comparator<Integer> com = (x,y) -> {
        System.out.println("函数式接口");
        return Integer.compare(x,y);
    };
    int compare = com.compare(1, 2);
    System.out.println(compare);
}

语法格式五

语法格式五:若 Lambda体中只有一条语句,return 和 大括号都可以省略不写

代码:

@Test
public void test10(){
    Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
}

语法格式六

语法格式六:lambda表达式的参数列表的数据类型可以省略不写,因为jvm编译器可以通过上下文进行类型的推断,大部分的情况下都是不写

@Test
public void test10(){
	Comparator<Integer> com = (Integer x,Integer y) -> Integer.compare(x,y);
}

3、函数式接口

函数式接口:接口中只有一个抽象方法的接口,称为函数式接口,可以使用注解@FunctionalInterface,可以检查这个接口是否是函数式接口

exp:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第9张图片

要是不是函数式接口,就会报错:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第10张图片

4、Java8中司法核心函数式接口

  • Consumer :消费型接口

    void accept(T t)

  • Supplier : 供给型接口

    T get()

  • Function : 函数型接口

    R apply(T t)

  • Predicate: 断言型接口

    boolean test(T t)

消费性接口

Consumer

@Test
    public void test11(){
        happy(100.0,(x)-> {
            x = x - 10;
            System.out.println("剩余"+x+"元");
        });
    }

    public void happy(Double money,Consumer<Double> con){
        con.accept(money);
    }

运行结果:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第11张图片

供给型接口

需求,产生指定个数的整数

代码:

@Test
    public void test12() {
        List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
        for (Integer integer : numList) {
            System.out.println(integer);
        }
    }

    public List<Integer> getNumList(int num, Supplier<Integer> sup) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            list.add(sup.get());
        }
        return list;
    }
}

函数型接口

需求:处理字符串

@Test
    public void test13(){
        String s = strHandler("\t\t\t\t\t\t 这是一个有很多空格的字符串", x -> x.trim());
        System.out.println(s);
    }

    public String strHandler(String str, Function<String, String> fun){
        String str1 = fun.apply(str);
        return str1;
    }

断言型接口

需求:将满足条件的字符串放在集合中

@Test
    public void test14(){
        List<String> list = Arrays.asList("hello","ahhaha","lambda","www","yxinmiracle","okkk","o");
        List<String> list1 = filterStr(list, x -> x.length() > 3);
        System.out.println(list1);

    }

    public List<String> filterStr(List<String> list, Predicate<String> pre) {
        List<String> strList = new ArrayList<>();
        for (String str : list) {
            if (pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }

输出:

[hello, ahhaha, lambda, yxinmiracle, okkk]

子接口

还有一些子接口:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第12张图片

5、方法引用

方法引用:若Lambda体中的内容有方法已经实现类,那么我们可以使用“方法引用”(可以理解为方法引用就是Lambda表达式的另外一种表现形式)

三种表现形式

  • 对象::实例方法名
  • 类::静态方法名
  • 类::实例方法名

对象::实例方法名

例1

举例:

使用消费型接口进行举例

lambda体中的功能已经有方法可以实现了,那么就可以使用方法引用的当时

但是有一个要求:

需要实现这个接口的抽象方法的参数列表,以及lambda中这个方法参数列表以及返回值类型要保持一致。

Consumer con = (x) -> System.out.println(x);

也就是说,上面这行代码,还可以写成下面这个样子:

Consumer con2 = System.out::println;

原因是,抽象方法的参数列表和返回值为:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第13张图片

lambda方法体中的println参数列表和返回值类型:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第14张图片

相同,所以可以这么使用

结果:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第15张图片

例2

再用供给型接口进行举例

查看Supplier接口中的抽象方法:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第16张图片

与我们Employee中的getName方法

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第17张图片

参数列表和返回值类型都相同

所以可以写成:

 @Test
    public void test16(){
        Employee emp = new Employee();
        emp.setName("hello java");
        // 形式1
        Supplier<String> sup = () -> emp.getName();
        System.out.println(sup.get());
        // 形式2
        Supplier<String> sup2 = emp::getName;
        System.out.println(sup2.get());
    }

输出结果:

hello java
hello java

类::静态方法名

@Test
    public void test17(){
        Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
        Comparator<Integer> com1 = Integer::compare;
    }

类::实例方法名

如果第一个参数是这个方法的调用者,第二个参数是这个调用方法的参数时,就可以使用类::实例方法名的方式

@Test
    public void test18(){
        BiPredicate<String,String> bp = (x,y)->x.equals(y);
        BiPredicate<String,String> bp2 = String::equals;
    }

6、构造器引用

格式:

ClassName::new

例子:

@Test
    public void test19(){
        Supplier<Employee> sup = ()->new Employee();
        // 构造器引用
        Supplier<Employee> sup2 = Employee::new;
    }

注:选择哪个构造器需要看抽象方法中函数的参数列表是怎么样的

7、Stream API

​ 流是数据渠道,用于操作数据源(集合、数组等)所产生的元素序列。“集合讲的是数据,流讲的是计算”

​ 注意:

  1. Stream 自己不会存储元素
  2. Stream 不会改变源对象。相反,热门会返回一个持有新结果的Stram
  3. Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

Stream操作的三个步骤

  • 创建Stream流

    一个数据源(如:集合、数组),获取一个流

  • 中间操作

    一个中间操作链,对数据源的数据进行处理

  • 终止操作(终端操作)

    一个终止操作,执行中间操作链,并产生结果

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第18张图片

创建流

可以通过Collection系列集合提供的stream()或parallelStream()

 @Test
    public void test20(){
        List lsit = new ArrayList<>();
        // 1.得到流
        Stream stream = lsit.stream();
    }

中间操作

  • filter-接收lambda,从流中排除某些元素
  • limit-截断流,使其元素不超过给定数量
  • skip(n)-跳过元素,返回一个人扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit互补
  • distinct-筛选,通过流所产生的元素的hashCode() 和 equals() 去除重复元素

只有遇到了终止操作才会进行执行中间操作

filter

@Test
public void test1() {
    employees.stream()
        .filter((e) -> {
            System.out.println("中间操作");
            return e.getAge() > 35;
        })
        .forEach(System.out::println);
}

结果:

中间操作
中间操作
Employee{name='李四', age=38, salary=5555.55}
中间操作
Employee{name='王五', age=50, salary=6666.66}
中间操作
中间操作

要是把终止操作forEach去掉

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第19张图片

就没有任何输出

其中,为什么foreach中可以写System.out::println,原因是:

foreach中的参数为consumer函数式接口:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第20张图片

而该consumer函数式接口的方法的参数列表和返回值类型为:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第21张图片

与println对应:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第22张图片

limit

只要前几个

@Test
public void test2(){
    employees.stream()
        .filter(e->e.getSalary()>5000)
        .limit(2)
        .forEach(System.out::println);
}

输出:

Employee{name='张三', age=18, salary=9999.99}
Employee{name='李四', age=38, salary=5555.55}

skip

扔掉前几个

@Test
    public void test3(){
        employees.stream()
                .filter(e->e.getSalary()>5000)
                .skip(2)
                .forEach(System.out::println);
    }

输出:

Employee{name='王五', age=50, salary=6666.66}
Employee{name='田七', age=8, salary=7777.77}

distinct

去重

数据改变:

List employees = Arrays.asList(
            new Employee("张三", 18, 9999.99),
            new Employee("李四", 38, 5555.55),
            new Employee("王五", 50, 6666.66),
            new Employee("赵六", 16, 3333.33),
            new Employee("田七", 8, 7777.77),
            new Employee("田七", 8, 7777.77),
            new Employee("田七", 8, 7777.77)
    );

要重写hashcode和equals方法

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第23张图片

代码:

@Test
    public void test4(){
        employees.stream()
                .filter(e->e.getSalary()>5000)
                .distinct()
                .forEach(System.out::println);
        System.out.println("---------------");
        employees.stream()
                .filter(e->e.getSalary()>5000)
                .forEach(System.out::println);
    }

输出:

Employee{name='张三', age=18, salary=9999.99}
Employee{name='李四', age=38, salary=5555.55}
Employee{name='王五', age=50, salary=6666.66}
Employee{name='田七', age=8, salary=7777.77}
---------------
Employee{name='张三', age=18, salary=9999.99}
Employee{name='李四', age=38, salary=5555.55}
Employee{name='王五', age=50, salary=6666.66}
Employee{name='田七', age=8, salary=7777.77}
Employee{name='田七', age=8, salary=7777.77}
Employee{name='田七', age=8, salary=7777.77}

映射

map-接收lambda,将元素换成其他形式或者提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射到成一个新的元素。

flatmap-接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连城一个流

map

@Test
    public void test5(){
        List<String> list = Arrays.asList("aaa","bbb","ddd","eee");

        list.stream()
                .map(s->s.toUpperCase()) // 到这一个步会变成一个新的流
                .forEach(System.out::println);
    }

结果:

AAA
BBB
DDD
EEE

需求:

提取员工姓名:

@Test
    public void test5(){
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
    }

flatmap

接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连城一个流

@Test
    public void test6(){
        List<String> list = Arrays.asList("aaa","bbb","ddd","eee");
        list.stream()
                .flatMap(TestStreamApi::filterCharater) // 到这一个步会变成一个新的流
                .forEach(System.out::println);
    }

输出:

a
a
a
b
b
b
d
d
d
e
e
e

他与map的区别在于,flatmap会把返回的流全部合成为一个新流,但是map不会,所以这里会看见需要两次foreach

@Test
    public void test6(){
        List<String> list = Arrays.asList("aaa","bbb","ddd","eee");
        list.stream()
                .map(TestStreamApi::filterCharater) // 到这一个步会变成一个新的流
                .forEach(sm->{
                    sm.forEach(System.out::println);
                });
    }

结果:

a
a
a
b
b
b
d
d
d
e
e
e

排序

  • sorted() – 自然排序(Comparable)
  • sorted(Comparator com)–定制排序(Compatator)

8、Stream API 终止操作

  • allMatch - 检查是否匹配所有元素
  • anyMatch - 检查是否至少匹配一个元素
  • noneMatch - 检查是否没有匹配所有元素
  • findFirst - 返回第一个元素
  • findAny - 返回当前流中的任意元素
  • count - 返回流中元素的总个数
  • max - 返回流中最大值
  • min - 返回流中最小值

allMatch

@Test
public void test8(){
    boolean flag = employees.stream()
        .allMatch(employee -> employee.getName().contains("张三"));
    System.out.println(flag);
}

输出:

false

anyMatch

@Test
    public void test9(){
        boolean flag = employees.stream()
                .anyMatch(employee -> employee.getName().contains("张三"));
        System.out.println(flag);
    }

输出:

true

noneMatch

@Test
    public void test10(){
        boolean flag = employees.stream()
                .noneMatch(employee -> employee.getName().contains("张三1"));
        System.out.println(flag);
    }

输出:

true

findFirst

@Test
    public void test11(){
        Optional<Employee> opt = employees.stream()
                .filter(employee -> employee.getAge() > 10)
                .findFirst();
        System.out.println(opt.get());
    }

输出:

Employee{name='张三', age=18, salary=9999.99}

findAny

@Test
    public void test12(){
        Optional<Employee> any = employees.parallelStream()
                .findAny();
        System.out.println(any.get());
    }

使用并行流parallelStream进行测试

输出:

Employee{name='田七', age=8, salary=7777.77}

count

@Test
    public void test13(){
        long count = employees.stream().count();
        System.out.println(count);
    }

输出:

7

max

@Test
    public void test14(){
        Optional<Employee> max = employees.stream()
                .max((e1, e2) -> e1.getAge() - e2.getAge());
        System.out.println(max.get());
    }

输出:

Employee{name='王五', age=50, salary=6666.66}

min

@Test
    public void test15(){
        Optional<Employee> min = employees.stream()
                .min((e1, e2) -> e1.getAge() - e2.getAge());
        System.out.println(min.get());
    }

输出:

Employee{name='田七', age=8, salary=7777.77}

reduce

可以将流中的元素反复结合起来,得到一个值

@Test
    public void test16(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);
    }

输出

55

需求:

计算员工的公司和

Optional<Double> reduce = employees.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
System.out.println(reduce.get());

collect

将流转换为其他形式。接收一个Collector接口的实现,用于Stream中元素做汇总的方法

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第24张图片

@Test
    public void test17() {
        List<Employee> collect = employees.stream()
                .limit(3)
                .collect(Collectors.toList());
        System.out.println(collect);
    }

输出:

[Employee{name='张三', age=18, salary=9999.99}, Employee{name='李四', age=38, salary=5555.55}, Employee{name='王五', age=50, salary=6666.66}]

其中Collectors还可以计算平均值,总和,最大值…

例如:

Double avgAge = employees.stream()
                .collect(Collectors.averagingInt(Employee::getAge));
System.out.println(avgAge);
collect分组

需求1:安装年龄分组

Map<Integer, List<Employee>> map = employees.stream()
                .collect(Collectors.groupingBy(Employee::getAge));
        System.out.println(map);

输出:

{16=[Employee{name='赵六', age=16, salary=3333.33}], 50=[Employee{name='王五', age=50, salary=6666.66}], 18=[Employee{name='张三', age=18, salary=9999.99}], 38=[Employee{name='李四', age=38, salary=5555.55}], 8=[Employee{name='田七', age=8, salary=7777.77}, Employee{name='田七', age=8, salary=7777.77}, Employee{name='田七', age=8, salary=7777.77}]}
collect多级分组

需求:按照薪水分组,然后再按年龄进行自定义分组

Map<Integer, Map<String, List<Employee>>> collect = employees.stream()
                .collect(Collectors.groupingBy(Employee::getAge, Collectors.groupingBy(e -> {
                    if (e.getAge() <= 35) {
                        return "青年";
                    } else if (e.getAge() <= 50) {
                        return "中年";
                    } else {
                        return "老年";
                    }
                })));
System.out.println(collect);

输出:

{16={青年=[Employee{name='赵六', age=16, salary=3333.33}]}, 49={中年=[Employee{name='田七', age=49, salary=7777.77}]}, 50={中年=[Employee{name='王五', age=50, salary=6666.66}]}, 18={青年=[Employee{name='张三', age=18, salary=9999.99}]}, 69={老年=[Employee{name='田七', age=69, salary=7777.77}]}, 38={中年=[Employee{name='李四', age=38, salary=5555.55}]}, 29={青年=[Employee{name='田七', age=29, salary=7777.77}]}}
collect分区

满足条件的一个区,不满足条件的一个区

Map<Boolean, List<Employee>> collect = employees.stream()
                .collect(Collectors.partitioningBy((e) -> e.getSalary() > 5000));
System.out.println(collect);

输出:

{false=[Employee{name='赵六', age=16, salary=3333.33}], true=[Employee{name='张三', age=18, salary=9999.99}, Employee{name='李四', age=38, salary=5555.55}, Employee{name='王五', age=50, salary=6666.66}, Employee{name='田七', age=29, salary=7777.77}, Employee{name='田七', age=49, salary=7777.77}, Employee{name='田七', age=69, salary=7777.77}]}
collect-summarizingDouble返回平均值最大值最小值…

代码:

DoubleSummaryStatistics dss = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));

可用方法:

Java8新特性(包含StreamApi、Lambda表达式用法、Java8中四大核心函数式接口、方法引用[java中::用法])_第25张图片

collect-join

把字符串拼接起来:

String collect = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining());
        System.out.println(collect);

输出:

张三李四王五赵六田七田七田七

把字符串用‘,’拼接起来:

String collect = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(","));
System.out.println(collect);

输出:

张三,李四,王五,赵六,田七,田七,田七

把字符串用‘,’拼接起来后,首尾用“==”拼接:

String collect = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(",","==","=="));
System.out.println(collect);

输出:

==张三,李四,王五,赵六,田七,田七,田七==

你可能感兴趣的:(后端,Java,Java进阶,java,后端)