Bootstrap

【Java8新特性 Lambda表达式-Stream API详解】

在这里插入图片描述

Lambda表达式

一、为什么使用Lambda表达式

Lambda是一个匿名函数,我么可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样传递)。可以写出更加简洁的、灵活的代码。作为一种更紧凑的代码风格,使java语言能力得到了提升
在这里插入图片描述

看不懂没关系,上代码我们先体会体会:

(补充:匿名内部类
匿名内部类去实现 Comparator接口:

public class TestLambda1{

    //匿名内部类
    public void test(){
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
    }
}

我们分析下,我们上述代码的核心代码,我们的需求是什么?我们需要的就是对:o1 和 o2 进行比较然后返回,所以我们的核心代码就这一行:Integer.compare(o1,o2); ,前面那一堆代码就是给这一句做铺垫,是不是很麻烦!

我们看看Lambda表达式是怎么完成需求的:

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

先别管上面Lambda表达式怎来的,后面会讲解,我们现在看到的是不是代码变得简洁

可能还有人会说:那我用匿名内部类实现接口,我可以用快捷键生成那几行代码啊,我为什么要新学习一个Lambda表达式
在这里插入图片描述
所以,用个案例,我们来看下Lambda表达式的强大

案例要求: 获取当前公司员工年龄大于35的员工信息

未使用Lambda表达式:

先写一个Employee类

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;
    }
//Get Set 方法自己补上
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

实现需求:

public class TestLambda1{
    List<Employee> employees = Arrays.asList( //数组转集合
            new Employee("A",10,25),
            new Employee("B",42,36),
            new Employee("C",45,45),
            new Employee("D",20,9)
    );
    @Test
    public void test3(){
        List<Employee> list = fEmployee(employees);
        for (Employee employee:list){
            System.out.println(employee);
        }
    }
    //获取当前公司员工年龄大于35的员工信息
    public List<Employee> fEmployee(List<Employee> list){
        List<Employee> emps = new ArrayList<>();
        for (Employee emp:list){
            if (emp.getAge()>=35){
                emps.add(emp);
            }
        }
        return emps;
    }
}

当然上面这样可以实现我们当前公司员工年龄大于35的员工信息的需求,但是如果我们的需求不断改变呢,比如:薪资大于多少的员工信息、名字是什么开头的员工信息,这些有一个共同点就是,我们只需要对:fEmployee(List list)代码里面的if判断条进行改变;但是就是为了改变这个条件,我们不得不再把这个方法再写一遍,然后改变if判断调节和业务要求一样,是不是好麻烦,所以要优化

优化方式1:策略设计模式

还是上面的业务需求,按照年龄过滤,我们改变设计模式

先定义一个接口:

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

定义一个类实现接口,根据业务实现接口方法:

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

设计的代码:

public class TestLambda1{
    List<Employee> employees = Arrays.asList( //数组转集合
            new Employee("A",10,25),
            new Employee("B",42,36),
            new Employee("C",45,45),
            new Employee("D",20,9)
    );
    //优化方式一
    @Test
    public void test4(){
        List<Employee> list = filterEmpliyee(employees,new FilteremployeeByAge());
        System.out.println(list);
    }
    public List<Employee> filterEmpliyee(List<Employee> list,MyPredicate<Employee> mp) {
        List<Employee> emps = new ArrayList<>();
        for (Employee employee : list) {
            if (mp.test(employee)) {
                emps.add(employee);
            }
        }
        return emps;
    }
}

上面这种设计模式有什么好处,就是说我们业务需求改变的时候,比方:薪资大于多少的员工信息、名字是什么开头的员工信息;我们不需要再另外写一个方法,然后改变我们的判断条件。
现在我们的做法就是我们写一个类实现我们的当前接口,然后根据业务需求实现接口方法,也就是说我们上面设计的就不用动了。
但这个设计模式也不好,因为来个新的业务要求,我们就要写个类去实现接口,麻烦,所以继续优化

优化方式2:匿名内部类

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

    //优化方式二:匿名内部类
    @Test
    public void test5(){
        List<Employee> list = filterEmpliyee(employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary()<=10;
            }
        });
        System.out.println(list);
    }

弊端:博客开头我们举例匿名内部类去实现 Comparator接口,分析了它的核心代码就一句,前面的代码都是多余的,给核心代码铺垫,上述代码也是一样的,我们的核心代码就是:employee.getSalary()<=10;

优化方式3:lambda表达式

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

    //优化方式二:匿名内部类
    @Test
    public void test5(){
        List<Employee> list = filterEmpliyee(employees, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary()<=10;
            }
        });
        System.out.println(list);
    }
    
    //Lambda表达式
    @Test
    public void test6(){
      List<Employee> list =   filterEmpliyee(employees, e->e.getSalary()<=500);
      list.forEach(System.out::print);
    }
二、Lambda表达式基础语法

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

左侧:Lambda表达式的参数列表
右侧:Lambda表达式所执行的功能

Lambda语法格式:Lambda语法

三、Lambda练习

学而不思则罔,思而不学则殆,所以举几个例子,看看Lambda的使用

案例一: 调用Collection.sort方法,通过制定排序比较两个Employee(先按年龄,年龄相同按照姓名比较),使用Lambda作为参数传递

员工类:

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;
    }
  //自己补上get set方法
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

实现:

public class TestLambda2 {
    @Test
    public void test1(){
        Collections.sort(employees,(e1,e2)->{
            if (e1.getAge()==e2.getAge()){
                return e1.getName().compareTo(e2.getName());
            }else {
                return Integer.compare(e1.getAge(),e2.getAge());
            }
        });
        System.out.println(employees);
    }

    List<Employee> employees = Arrays.asList( //数组转集合
            new Employee("A",10,25),
            new Employee("B",42,36),
            new Employee("C",45,45),
            new Employee("D",20,9)
    );
}

案例二:
1.声明函数式接口,接口中声明抽象方法,public String getvalue(String str);
2.声明类TestLambda,类中编写方法使用接口作为参数,将一个字符串变为大写,并作为方法的返回值

我们先写一个接口:

@FunctionalInterface
public interface MyFunction {
    public String getvalue(String s);
}

实现:

  @Test
    public void test2(){
       String s1 =  strHandler("abc",s->s.toUpperCase());
        System.out.println(s1);
    }
    //需求:用于处理字符串的方法
    public String strHandler(String s,MyFunction mf){
        return mf.getvalue(s);
    }

案例三:
1.声明一个带两个泛型的函数式接口,泛型类型为<T,R>,T为参数 R 返回值
2.接口中声明对应的方法
3.在创建的类中声明方法,使用接口作为参数,计算两个Long型参数的和

我们先写一个接口:

public interface MyFunctoin2<T,R> {
    public R getValue(T t1,T t2);
}

声明方法:

 //需求:对于两个long类型计算
    @Test
    public void test3(){
        op(100L,200L,(x,y)->x+y);
    }
    public void op(Long l1,Long l2,MyFunctoin2<Long,Long> mf){
        System.out.println(mf.getValue(l1,l2));
    }

在这里插入图片描述

但是上面几个案例都有个问题就是:我们要用Lambda表达式,我们都是先写一个接口去支持Lambda的实现,是不是好麻烦,所以java给我们内置了核心的函数式接口

四、四大核心函数式接口

在这里插入图片描述
分别用上述接口举一个案例:

public class TestLambda3 {

   // 1、Consumersumer<T> 消费型接口
    @Test
    public void test1(){

        happy(1000,m-> System.out.println("消费"));
    }
    public void happy(double money,Consumer<Double> con){
        con.accept(money);
    }

    //2、Supplier<T> 供给型接口
    @Test
    public void test2(){
        //需求:随机获取10个数字
        List list = getNumList(10,()->(int)(Math.random()*10));
        System.out.println(list);
    }
    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;
    }

    //3、Function<T,R> 函数型接口
    @Test
    public void test3(){
        //需求:去掉首尾空格
        String s = strHandler(" hello ",str->str.trim());
        System.out.println(s);
    }
    public String strHandler(String s,Function<String,String> fun){
        return fun.apply(s);
    }

   // 4、Predicate<T,boolean> 断定型接口
    @Test
    public void test4(){
        //需求:String 长度大于3
        List<String> list =  Arrays.asList("ABC","BCDE","CEFDG");
        System.out.println(filterStr(list,s->s.length()>3));
    }
    public List<String> filterStr(List<String> list,Predicate<String> pre){
        List<String> list1 = new ArrayList<>();
        for (String s:list){
            if (pre.test(s)){
                list1.add(s);
            }
        }
        return list1;
    }
}

这四大内置核心函数式接口算是开发中常用的,除开这四个,其实Java8还帮我们提供了其它的函数式接口,理解了这四大核心函数式接口,其它的理解起来也就相当容易了。我们可以去java.util.function包下面查看具体的接口:
在这里插入图片描述

五、方法引用与构造器引用

在这里插入图片描述

(1)对象的引用 :: 实例方法名

// 1. 对象的引用 :: 实例方法名
    @Test
    public void test() {
        // ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("hello");

        PrintStream ps = System.out;
        Consumer<String> con2 = (x) -> ps.println(x);
        con2.accept("hello");

        // 对象的引用 :: 实例方法名
        PrintStream ps2 = System.out;
        Consumer<String> con3 = ps2::println;
        con3.accept("hello");

    }

    @Test
    public void test2() {
        Employee emp = new Employee();

        Supplier<String> sup = () -> emp.getName();
        String str = sup.get();
        System.out.println(str);

        // 对象的引用 :: 实例方法名
        Supplier<Integer> sup2 = emp::getAge;
        Integer num = sup2.get();
        System.out.println(num);
    }

(2)类名 :: 静态方法名

// 2. 类名 :: 静态方法名
    @Test
    public void test3() {

        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
        // int compare = com.compare(2, 3);
        // System.out.println(compare);

        // 类名 :: 静态方法名
        Comparator<Integer> com2 = Integer::compare;
    }

(3)类名 :: 实例方法名

// 3. 类名 :: 实例方法名
    @Test
    public void test4() {
        BiPredicate<String, String> bp = (x, y) -> x.equals(y);
        // 使用要求: 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式:
        // ClassName::MethodName
        BiPredicate<String, String> bp2 = String::equals;
        // boolean test = bp2.test("hello", "hello");
        // System.out.println(test);
    }
    
        
    }

在这里插入图片描述

  //二、构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!
     //1. 类名 :: new
    //ClassName::new
    //注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致、
    @Test
    public void test5() {
        Supplier<Employee> sup =()->new Employee();
        
        //构造器引用方式
        Supplier<Employee> sup2=Employee::new;
        Employee emp=sup2.get();
        System.out.println(emp);
    }
    @Test
    public void test6() {
        Function<Integer,Employee> fun=(x)->new Employee(x);
        
        Function<Integer,Employee> fun2=Employee::new;
        Employee emp=fun2.apply(101);
        System.out.println(emp);
        //需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致、
        //这里的参数列表,就对应employee的构造器的列表一样
        BiFunction<Integer,Integer,Employee> bif=Employee::new;
        Employee apply = bif.apply(2, 3);
        System.out.println(apply);
        
    }

Stream API

一、Stream简介和创建

Java8的两个重大改变,一个是Lambda表达式,另一个就是本节要讲的Stream API表达式。Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作,在新版的JPA中,也已经加入了Stream

Stream API将处理的数据源看做一种Stream(流),Stream(流)在Pipeline(管道)中传输和运算,支持的运算包含筛选、排序、聚合等,当到达终点后便得到最终的处理结果

几个关键概念:
1.元素 Stream是一个来自数据源的元素队列,Stream本身并不存储元素。
2.数据源(即Stream的来源)包含集合、数组、I/O channel、generator(发生器)等。
3.聚合操作 类似SQL中的filter、map、find、match、sorted等操作
4.管道运算 Stream在Pipeline中运算后返回Stream对象本身,这样多个操作串联成一个Pipeline,并形成fluent风格的代码。这种方式可以优化操作,如延迟执行(laziness)和短路( short-circuiting)。
5.内部迭代 不同于java8以前对集合的遍历方式(外部迭代),Stream API采用访问者模式(Visitor)实现了内部迭代。
6.并行运算 Stream API支持串行(stream() )或并行(parallelStream() )的两种操作方式

1、Stream的操作步骤
Stream有如下三个操作步骤:
一、创建Stream
从一个数据源,如集合、数组中获取流。
二、中间操作
一个操作的中间链,对数据源的数据进行操作。
三、终止操作
一个终止操作,执行中间操作链,并产生结果

1.创建Stream的几种方式

public class TestStreamAPI1 {
    //创建Stream

    //方式一:可以通过Collection系列集合提供的stream()或者parallelStream
    @Test
    public void test() {
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();


        //方式二:可以通过Arrays中的静态方法 stream()获取数组流
        Employee[] emps = new Employee[10];
        Stream<Employee> stream2 = Arrays.stream(emps);

        //方式三:可以通过Stream类中的静态方法of()
        Stream<String> stream3 = Stream.of("A", "B");

        //方式三: 创建无限流
        //迭代
        Stream<Integer> stream4 = Stream.iterate(0, x -> x + 1);
        stream4.limit(10).forEach(System.out::println);
        //生成
        Stream.generate(()->Math.random())
                .limit(5)
                .forEach(System.out::println);
    }
}

二、Stream中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值” 。Stream的中间操作是不会有任何结果数据输出的

Stream的中间操作在整体上可以分为:筛选与切片、映射、排序。接下来,我们就分别对这些中间操作进行简要的说明
在这里插入图片描述
一、筛选与切片

我们写代码来理解理解上面的中间操作
1.filter

public class TestStreamAPI2 {

    List<Employee> employees = Arrays.asList( //数组转集合
            new Employee("A",10,25),
            new Employee("B",42,36),
            new Employee("C",45,45),
            new Employee("D",20,9)
    );
    //中间操作
    @Test
    public  void test1(){
        Stream <Employee>stream1 = employees.stream()//这里我们获取年龄大于35的
                .filter(e->e.getAge()>35);
        stream1 .forEach(System.out::println);
    }
}

我们,在执行终止语句之后,一边迭代,一边打印,而我们并没有去迭代上面集合,其实这是内部迭代,由Stream API 完成

下面我们来看看外部迭代,也就是我们人为得迭代

//外部迭代
Iterator<Person> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

2.limit

   @Test
    public void test2(){
         employees.stream()
                 //.filter(e->e.getSalary()>20)
                 .filter(e->{
                     System.out.println("短路");
                     return e.getSalary()>20;
                 })
                .limit(2)
                .forEach(System.out::println);
    }

在这里,我们可以配合其他得中间操作,并截断流,使我们可以取得相应个数得元素。而且在上面计算中,只要发现有2条符合条件得元素,则不会继续往下迭代数据,可以提高效率。

3.skip()方法

 @Test
    public void test3(){
        employees.stream()
                .filter(e->e.getSalary()>20)
                .skip(2)
                .forEach(System.out::println);
    }
//跳过前2个值
list.stream().skip(2).forEach(System.out :: println);

4.distinct()方法

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

这里有一个需要注意的地方:distinct 需要实体中重写hashCode()和 equals()方法才可以使用

二、映射
关于映射相关的方法如下表所示
在这里插入图片描述
1.map()方法

//将流中每一个元素都映射到map的函数中,每个元素执行这个函数,再返回
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf);

//获取Person中的每一个人得名字name,再返回一个集合
List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList())

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

 @Test
    public void testFlatMap () {
        StreamAPI_Test s = new StreamAPI_Test();
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
        list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println);

        //如果使用map则需要这样写
        list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> {
            e.forEach(System.out::println);
        });
    }

    /**
     * 将一个字符串转换为流
     */
    public Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character ch : str.toCharArray()) {
            list.add(ch);
        }
        return list.stream();
    }

其实map方法就相当于Collaction的add方法,如果add的是个集合得话就会变成二维数组,而flatMap 的话就相当于Collaction的addAll方法,参数如果是集合得话,只是将2个集合合并,而不是变成二维数组

三、Stream排序

在这里插入图片描述
从上述表格可以看出:sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序

先来看Java8中Stream接口对于sorted()方法的声明,如下所示:

Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
// 自然排序
List<Employee> persons = list.stream().sorted().collect(Collectors.toList());

//定制排序
List<Employee> persons1 = list.stream().sorted((e1, e2) -> {
    if (e1.getAge() == e2.getAge()) {
        return 0;
    } else if (e1.getAge() > e2.getAge()) {
        return 1;
    } else {
        return -1;
    }
}).collect(Collectors.toList());
四、Stream插找和匹配

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

  List<Employee> employees = Arrays.asList(//
            new Employee(20, "张三", 5000.35, Status.FREE), //
            new Employee(40, "李四", 6500.63, Status.BUSY), //
            new Employee(30, "王五", 4000.93, Status.FREE), //
            new Employee(50, "赵六", 9005.36, Status.BUSY), //
            new Employee(10, "马七", 1050.93, Status.VOCATION), //
            new Employee(20, "朱八", 3000.73, Status.BUSY)//
    );

    @Test
    public void test1() {

        // 1、allMatch—检查是否匹配所有元素
        boolean allMatch = employees.stream().allMatch((e) -> e.getStatus().equals(Status.FREE));
        System.out.println(allMatch);

        // 2、anyMatch—检查是否至少匹配一个元素
        boolean anyMatch = employees.stream().anyMatch((e) -> e.getStatus().equals(Status.FREE));
        System.out.println(anyMatch);

        // 3、noneMatch—检查是否没有匹配所有元素
        boolean noneMatch = employees.stream().noneMatch((e) -> e.getStatus().equals(Status.FREE));
        System.out.println(noneMatch);

        // 4、findFirst—返回第一个元素
        Optional<Employee> findFirst = employees.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())).findFirst();
        System.out.println(findFirst.get());

        // 5、findAny—返回当前流中的任意元素
        Optional<Employee> findAny = employees.stream().filter((e) -> e.getStatus().equals(Status.FREE)).findAny();
        System.out.println(findAny.get());

        // 6、count—返回流中元素的总个数
        long count = employees.stream().count();
        System.out.println(count);

        // 7、max—返回流中最大值
        Optional<Employee> max = employees.stream().max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get());

        // 8、min一返回流中最小值
        Optional<Double> min = employees.stream().map(Employee::getSalary).min(Double::compare);
        System.out.println(min.get());
    }
五、Stream归约和收集

在这里插入图片描述
在这里插入图片描述

import java.util.*;
import java.util.stream.Collectors;

public class TestStreamAPI5 {
    /*
    规约:reduce(BinaryOperator)- 可以将流中元素反复结合起来,得到一个值
     */
    List<Employee> employees = Arrays.asList(
            new Employee("张三",18,9999.99, Employee.Status.FREE),
            new Employee("李四",58,5555.55, Employee.Status.BUSY),
            new Employee("王五",26,3333.33, Employee.Status.VOCATION),
            new Employee("赵六",36,6666.66, Employee.Status.FREE),
            new Employee("田七",12,8888.88, Employee.Status.BUSY),
            new Employee("田七",12,8888.88, Employee.Status.BUSY)
    );

    @Test
    public void test1(){
        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);
        System.out.println("--------------------------------");

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

    /*
    收集:collect-将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
     1.总数
     2.平均值
     3.总和
     4.最大值
     5.最小值
     =============
     6.分组
     =============
     7.多级分组
     =============
     8.分区(满足条件一个区,不满足条件一个区)
     */
    @Test
    public void test2(){
        List<String> list = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        list.forEach(System.out::println);
        System.out.println("================toList结束============");

        Set<String> set = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());
        set.forEach(System.out::println);
        System.out.println("================toSet结束============");

        HashSet<String> hashSet =  employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::println);
        System.out.println("================toCollection结束============");
    }

    @Test
    public void test3(){
        //总数
       Long count = employees.stream()
               .collect(Collectors.counting());
        System.out.println(count);

        //平均值
       Double money =  employees.stream()
                .collect(Collectors.averagingDouble(Employee::getMoney));
        System.out.println(money);

        //总和
        Double moneySum = employees.stream()
                .collect(Collectors.summingDouble(Employee::getMoney));
        System.out.println(moneySum);

        //最大值
        Optional<Employee> optionalEmployee = employees.stream()
                .collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getMoney(),e2.getMoney())));
        System.out.println(optionalEmployee.get());

        //最小值
        Optional<Double> min = employees.stream()
                .map(Employee::getMoney)
                .collect(Collectors.minBy(Double::compareTo));
        System.out.println(min);
    }

}
六、Stream练习

练习1:

public class TestStream {
    //1、给定数字 1 2 3 4 5 ,返回平方和列表
    @Test
    public void test1(){
        Integer[]nums = new Integer[]{1,2,3,4,5};
        Arrays.stream(nums)
                .map(x->x*x)
                .forEach(System.out::println);
    }
    List<Employee> employees = Arrays.asList( //数组转集合
            new Employee("A",0,25),
            new Employee("B",42,36),
            new Employee("B",41,36),
            new Employee("C",45,45),
            new Employee("C",45,45),
            new Employee("D",20,9)
    );
    //2、用map和reduce完成,数一数流中多少个Employee
    @Test
    public void test2(){
        Optional<Integer> c = employees.stream()
                .map(e->1)
                .reduce(Integer::sum);
        System.out.println(c.get());
    }
}

练习2:
先创建交易员和交易类

//交易员
public class Trader {
    private final String name;
    private final String city;


    public Trader(String name, String city) {
        this.name = name;
        this.city = city;
    }

    public String getName() {
        return name;
    }


    public String getCity() {
        return city;
    }


    @Override
    public String toString() {
        return "Trader{" +
                "name='" + name + '\'' +
                ", city='" + city + '\'' +
                '}';
    }
}
//交易类
public class Transaction {

    private final Trader trader;
    private final int year;
    private final int value;

    private Currency currency;

    public Currency getCurrency() {
        return currency;
    }

    public void setCurrency(Currency currency) {
        this.currency = currency;
    }

    public Transaction(Trader trader, int year, int value) {
        this.trader = trader;
        this.year = year;
        this.value = value;
    }

    public Trader getTrader() {
        return trader;
    }


    public int getYear() {
        return year;
    }


    public int getValue() {
        return value;
    }


    @Override
    public String toString() {
        return "Transaction{" +
                "trader=" + trader +
                ", year=" + year +
                ", value=" + value +
                '}';
    }
}

测试类:


public class TestTransaction {

    List<Transaction> transactions = null;

    @Before
    public void before() {
        Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario", "Milan");
        Trader alan = new Trader("Alan", "Cambridge");
        Trader brian = new Trader("Brian", "Cambridge");

        transactions = Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000),
                new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(mario, 2012, 950)
        );
    }


    @Test  //找出2011年发生的所有交易 交易额低-》高
   public void test1(){
        transactions.stream()
                .filter(t->t.getYear()==2011)
                .sorted((t1,t2)->Integer.compare(t1.getValue(),t2.getValue()))
                .forEach(System.out::println);

    }
    @Test  //交易员都在哪些城市工作过
    public void test2(){
        transactions.stream()
                .map(t->t.getTrader().getCity())
                .distinct()
                .forEach(System.out::println);
    }

    @Test //所有来自剑桥的交易员 按姓名排序
    public void test3(){
        transactions.stream()
                .filter(t->t.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getTrader)
                .sorted((t1,t2)->t1.getName().compareTo(t2.getName()))
                .distinct()
                .forEach(System.out::println);
    }
    @Test //返回交易员的姓名字符串 按字母排序
    public void test4(){
        transactions.stream()
                .map(t->t.getTrader().getName())
                .sorted()
                .forEach(System.out::println);
        System.out.println("--------------");
        String s = transactions.stream()
                .map(t->t.getTrader().getName())
                .sorted()
                .reduce("",String::concat);
        System.out.println(s);
        System.out.println("---------------");
        transactions.stream()
                .map(t->t.getTrader().getName())
                .flatMap(TestTransaction::filterChara)
                .sorted((s1,s2)->s1.compareToIgnoreCase(s2))
                .forEach(System.out::print);

    }
    public static Stream<String> filterChara(String s){
        List<String> list = new ArrayList<>();
        for (Character c:s.toCharArray()){
            list.add(c.toString());
        }
        return list.stream();
    }

    @Test //有没有交易员在米兰工作
    public void test5(){
        boolean b = transactions.stream()
                .anyMatch(t->t.getTrader().getCity().equals("Milan"));
        System.out.println(b);

    }

    @Test   //  打印在剑桥交易员的所有交易额
    public void test6(){
       Optional<Integer> o =  transactions.stream()
                .filter(e->e.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getValue)
                .reduce(Integer::sum);
        System.out.println(o.get());
    }

    @Test //所有交易额最高的是多少
    public void test7(){
        Optional<Integer> o  = transactions.stream()
                .map(t->t.getValue())
                .max(Integer::compareTo);
        System.out.println(o.get());
    }

    @Test //最小交易
    public void test8(){
        Optional<Transaction> o = transactions.stream()
                .min((t1,t2)->Integer.compare(t1.getValue(),t2.getValue()));
        System.out.println(o.get());
    }
    
}
;