四、streamAPI

目录

第一节:java8 Stream基本概念

一、java8Stream概述

1、stream定义

2、图解stream

3、stream流到底是什么?

4、stream注意事项

第二节:StreamApi之创建流

一、Stream创建方式

1、通过collection系列集合获取stream

2、通过Arrays获取stream

3、通过Stream类获取stream

4、无限流

二、实例演示创建Stream

1、代码实现

2、运行效果

第三节:StreamApi之筛选与切片

一、stream的中间操作概述

二、stream的中间操作之筛选和切片

1、filter(Predicate p)方法

2、distinct()方法

3、limit(long maxSize)方法

4、skip(long n)方法

三、实例演示stream的中间操作之筛选和切片

1、实例演示filter(Predicate p)方法

2、实例演示limit(long maxSize)方法

3、实例演示distinct()方法

4、实例演示skip(long n)方法

第四节:StreamApi之映射

一、stream的映射概述

1、map映射

2、flatMap映射

二、实例演示stream的映射操作

1、演示map映射

2、演示flatMap映射

第五节:StreamApi之排序

一、stream的排序概述

二、演示stream的排序操作

1、演示自然排序

2、演示定制排序

第六节:StreamApi之终止操作

一、stream的终止操作

二、查找与匹配

1、allMatch方法

2、anyMatch方法

3、noneMatch方法

4、findFirst方法

5、findAny方法

6、count方法

7、max方法

8、min方法

三、实例演示

1、修改数据源

2、代码实现

3、运行效果

第七节:StreamApi之归纳与搜集

一、归约

1、reduce(T identity,BinaryOperator)和reduce(BinaryOperator)方法

2、实例演示

二、搜集

1、collect方法

2、collect方法实例演示

第八节:StreamApi之小小练习题

一、练习1

1、需求

2、代码实现

3、运行效果

二、练习2

1、需求

2、实现

3、运行效果

三、练习3(综合题)

1、找出2011年发生的所有交易,并按交易额排序(从低到高)

2、交易员都在哪些不同的城市工作过

3、查找所有来自剑桥的交易员,并按姓名排序

4、返回所有交易员的姓名字符串,按字符顺序排序

5、有没有交易员是在米兰工作的

6、打印生活在剑桥的交易员的所有交易额

7、所有交易中,最高的交易额是多少

8、找到交易额最小的交易

第九节:并行流与顺序流

一、并行流

1、并行流概念

2、fork/join框架(原始的手段)

3、fork/join框架示例演示

4、java8并行流示例演示


第一节:java8 Stream基本概念

Java 8中有两个最为重要的改变,一个是lambad表达式;另外一个则是StreamAPI(java.util.stream.*);那么接下来我们就来了解一下这个streamAPI的内容!

 

一、java8Stream概述

1、stream定义

Stream是java8中处理集合的关键抽象概念,他可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

使用StreanAPI对集合数据进行操作,就类似于使用sql执行的数据库查询。也可以使用streamAPI来合并行执行操作。简而言之,streamAPI提供了一种高效且易于使用的处理数据的方式。

2、图解stream

 

 

如上图所示,java8中的stream操作的流程,首先需要创建一个流,这个流的数据源可以是集合,也可以是数组。然后这个流可以经过一系列的流水线式的的操作,最终生成一个新的流!而对于数据源则没有任何影响!

3、stream流到底是什么?

结合前面的讲解,我们来总结一下stream到底是什么呢?

Stream就是数据管道,用于操作数据源(集合、数组等)所生成的元素序列。

集合的重点在于数据的存储,而流的重点在于计算!

4、stream注意事项

1)、stream自己不会存储元素;

2)、stream不会改变源数据对象,相反的,他会生成并返回一个持有结果的新的stream;

3)、stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行;

5、stream的操作步骤

如上图所示,stream的操作步骤大致上可以分为如下几个步骤:

1)、创建stream

通过一个数据源,如集合、数组,来获取一个流实例;

2)、中间操作

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

3)、终止操作(终端操作)

一个终止操作出现后,会执行中间操作链,并产生结果;

 

第二节:StreamApi之创建流

一、Stream创建方式

Stream的创建方式一共有四种,分别是:

1、通过collection系列集合获取stream

通过collection系列集合提供的stream()方法获取串行流,或者通过parallelStream()方法获取并行流;

2、通过Arrays获取stream

通过Arrays中的静态方法stream获取一个数组流;

3、通过Stream类获取stream

通过Stream类中的静态方法of()获取一个流;

4、无限流

无限流又分为迭代和生成两种方式创建;

 

二、实例演示创建Stream

1、代码实现

我们在这里就一起演示这四种创建方式:

我们在这里就一起演示这四种创建方式:

packagestreamAPI;

 

importjava.util.ArrayList;

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

 

import 为什么要使用lambad表达式.entity.Employee;

 

/**

 *一、stream的三个操作步骤

 * 1.创建stream

 * 2.中间操作

 * 3.终止操作(终端操作)

 *

 *

 */

public classTestStreamAPI1 {

 

    

     /**

      *创建流

      *我们创建stream流有四种方式

      *如下:

      */

     public static voidtestCreateStream() {

        

        

         //1.通过collection系列集合提供的stream()方法获取串行流,或者通过parallelStream()方法获取并行流

         List<String>list=newArrayList<String>();

         Stream<String>stream1=list.stream();

        

         //2.通过Arrays中的静态方法stream获取一个数组流

         Employee[]emps=newEmployee[10];

         Stream<Employee>stream2= Arrays.stream(emps);

        

         //3.通过Stream类中的静态方法of()获取一个流

         Stream<String>stream3= Stream.of("aa","bb","cc","dd","ee");

        

         //4.创建无限流

         //1).迭代

         Stream<Integer>stream4= Stream.iterate(0,(x)->x+2);

         stream4.limit(5).forEach(System.out::println);

        

         //2).生成

         Stream<Double>stream5= Stream.generate(()->Math.random());

         stream5.limit(5).forEach(System.out::println);

        

     }

    

     public static voidmain(String[]args) {

         testCreateStream();

     }   

}

2、运行效果

0

2

4

6

8

0.04711765843563487

0.8986789912903362

0.31479564241549685

0.14205863575868716

0.8298402400671744

其实这个运行效果你可以不看的,因为这里主要演示的还是创建流的操作;

 

第三节:StreamApi之筛选与切片

那么我们前面已经将stream的创建方式介绍过了,接下来我们看一下stream的中间操作;

一、stream的中间操作概述

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,成为惰性求值,或称延迟加载;

在stream中的中间操作使用到了内部迭代,这个迭代是由streamAPI帮我们做的;

与内部迭代相对应的就是外部迭代了,其实说白了就是我们自己写的迭代代码,就是外部迭代!

其具体的操作可以分为很多种,比如筛选和切片、映射、排序等等!

在这一节课我们先来了解一下筛选和切片!

 

二、stream的中间操作之筛选和切片

筛选和切片常用的方法如下:

1、filter(Predicate p)方法

该方法用于接收lambad,从流中排除某些元素;

2、distinct()方法

该方法用于筛选,通过流所产生的元素的hashCode()和equals()去除重复元素;

3、limit(long maxSize)方法

该方法用于截断流,使其元素不超过给定数量;

4、skip(long n)方法

该方法用于跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补;

 

三、实例演示stream的中间操作之筛选和切片

1、实例演示filter(Predicate p)方法

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

 

import 为什么要使用lambad表达式.entity.Employee;

 

public classTestStreamAPI2 {

 

     //创建数据

     private staticList<Employee>employees= Arrays.asList(

         newEmployee("张三",18,9999.99),

         newEmployee("李四",38,5555.55),

         newEmployee("王五",50,6666.66),

         newEmployee("赵六",16,3333.33),

         newEmployee("田七", 8,7777.77)

     );

    

     public static voidtestFilter() {

        

         //获取员工年龄大于等于35岁的员工信息

         Stream<Employee>stream1=employees.stream().filter((e)->e.getAge()>=35);

         //为了能看到效果,我们暂时先给他一个终止操作,后期会详细讲解终止操作

         stream1.forEach(System.out::println);

     }

 

     public static voidmain(String[]args) {

         testFilter();

     }   

}

2)、运行效果

Employee [name=李四, age=38, salary=5555.55]

Employee [name=王五, age=50, salary=6666.66]

2、实例演示limit(long maxSize)方法

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

 

import 为什么要使用lambad表达式.entity.Employee;

 

public classTestStreamAPI2 {

 

     public static voidtestLimit() {

         //获取集合中员工工资大于5000的前两个员工信息

         employees.stream().

                    filter((e)->e.getSalary()>5000).

                    limit(2).

                    forEach(System.out::println);

     }

     public static voidmain(String[]args) {

         testLimit();

     }   

}

2)、运行效果

Employee [name=张三, age=18, salary=9999.99]

Employee [name=李四, age=38, salary=5555.55]

3)、备注

Limit方法支持短路操作,一旦获取到满足条件的数据后,后面没有迭代的流,便不会再进行迭代了!这样的话,在某种场景下是可以提高效率的!和我们短路与&&和短路或||是一样的;

3、实例演示distinct()方法

1)、代码实现

packagestreamAPI;

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

import 为什么要使用lambad表达式.entity.Employee;

public classTestStreamAPI2 {

     public static voidtestDistinct() {

         employees.stream().

              distinct().

              forEach(System.out::println);

     }

 

     public static voidmain(String[]args) {

         testDistinct();

     }   

}

2)、运行效果

Employee [name=张三, age=18, salary=9999.99]

Employee [name=李四, age=38, salary=5555.55]

Employee [name=王五, age=50, salary=6666.66]

Employee [name=赵六, age=16, salary=3333.33]

Employee [name=田七, age=8, salary=7777.77]

3)、备注

使用去重方法的时候,必须重新需要去重元素中的hashCode和equals函数;

4、实例演示skip(long n)方法

1)、代码实现

packagestreamAPI;

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

import 为什么要使用lambad表达式.entity.Employee;

public classTestStreamAPI2 {

      public static voidtestSkip() {

           //获取集合中员工工资大于5000的后两个员工信息

           employees.stream().

                 filter((e)->e.getSalary()>5000).

                 skip(2).

                 forEach(System.out::println);

      }

      public static voidmain(String[]args) {

           testSkip();

      }

}

2)、运行效果

Employee [name=王五, age=50, salary=6666.66]

Employee [name=田七, age=8, salary=7777.77]

 

第四节:StreamApi之映射

一、stream的映射概述

映射操作分为map和flatMap两种:

1、map映射

接受到lambad后,将元素转换成其他形式或提取信息,接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素;

2、flatMap映射

接受一个函数作为参数,并将流中的每个值都换成另外一个流,然后把左右流合并成一个新的流;

 

二、实例演示stream的映射操作

1、演示map映射

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

 

import 为什么要使用lambad表达式.entity.Employee;

 

public classTestStreamAPI3 {

    

     //创建数据

     private staticList<Employee>employees= Arrays.asList(

         newEmployee("张三",18,9999.99),

         newEmployee("李四",38,5555.55),

         newEmployee("王五",50,6666.66),

         newEmployee("赵六",16,3333.33),

         newEmployee("田七", 8,7777.77),

         newEmployee("田七", 8,7777.77)

     );

    

    

     public static voidtestMap() {

         System.out.println("需求1:将给定集合中的数据专为大写");

         List<String>list= Arrays.asList("aa","bb","cc","dd","ee");

         list.stream().map((str)->str.toUpperCase()).forEach(System.out::println);

        

         System.out.println("需求2:提取员工姓名");

         employees.stream().map(Employee::getName).forEach(System.out::println);

     }

    

     public static voidmain(String[]args) {

         testMap();

     }

    

}

2)、运行效果

需求1:将给定集合中的数据专为大写

AA

BB

CC

DD

EE

需求2:提取员工姓名

张三

李四

王五

赵六

田七

田七

2、演示flatMap映射

首先,我们来看一下为什么要有flatMap这个操作;

我们先给定一个需求:将指定集合中的字符串拆分为字符,并打印输出之;

下面我们来看一下使用map操作的实现方式:

1)、代码实现

packagestreamAPI;

 

importjava.util.ArrayList;

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

 

import 为什么要使用lambad表达式.entity.Employee;

 

public classTestStreamAPI3 {

    

     //创建数据

     private staticList<Employee>employees= Arrays.asList(

         newEmployee("张三",18,9999.99),

         newEmployee("李四",38,5555.55),

         newEmployee("王五",50,6666.66),

         newEmployee("赵六",16,3333.33),

         newEmployee("田七", 8,7777.77),

         newEmployee("田七", 8,7777.77)

     );

    

     public static voidtestMap2() {

         List<String>list= Arrays.asList("aa","bb","cc","dd","ee");

         Stream<Stream<Character>>stream=list.stream().map(TestStreamAPI3::filterCharacter);

         stream.forEach(sm->{

              sm.forEach(System.out::println);

         });

     }

    

     /**

      *为了演示flatMap,

      *我们创建一个提取字符串中字符的操作,

      *最终使其返回一个stream

      *@paramstr

      *@return

      */

     public staticStream<Character> filterCharacter(Stringstr){

         List<Character>list=newArrayList<Character>();

         for(Characterch:str.toCharArray()) {

              list.add(ch);

         }

         return list.stream();

     }

    

     public static voidmain(String[]args) {

         testMap2();

     }

}

2)、运行效果

a

a

b

b

c

c

d

d

e

e

3)、问题

可以看出来这个效果是没有问题的,但是在遍历的时候,是不是要遍历两次啊,这是不是很不符合我们代码的简洁约束啊,所以这个时候就需要使用flatMap操作来对其进行优化了;

下面我们来使用flatMap来演示:

1)、代码实现

packagestreamAPI;

importjava.util.ArrayList;

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

import 为什么要使用lambad表达式.entity.Employee;

public classTestStreamAPI3 {

    

     public static voidtestFlatMap() {

         List<String>list= Arrays.asList("aa","bb","cc","dd","ee");

         Stream<Character>flatMapStream=list.stream().flatMap(TestStreamAPI3::filterCharacter);

         flatMapStream.forEach(System.out::println);

     }

    

     /**

      *为了演示flatMap,

      *我们创建一个提取字符串中字符的操作,

      *最终使其返回一个stream

      *@paramstr

      *@return

      */

     public staticStream<Character> filterCharacter(Stringstr){

         List<Character>list=newArrayList<Character>();

         for(Characterch:str.toCharArray()) {

              list.add(ch);

         }

         return list.stream();

     }

    

     public static voidmain(String[]args) {

         testFlatMap();

     }

}

2)、运行效果

a

a

b

b

c

c

d

d

e

e

 

第五节:StreamApi之排序

一、stream的排序概述

排序操作分为sorted自然排序和sortes(Compartor com)定制排序两种:

 

二、演示stream的排序操作

1、演示自然排序

1)、代码实现

packagestreamAPI;

 

importjava.util.ArrayList;

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

 

import 为什么要使用lambad表达式.entity.Employee;

 

public classTestStreamAPI4 {

 

     public static voidtestSorted() {

         List<String>list= Arrays.asList("aa","bb","cc","dd","ee");

         list.stream().sorted().forEach(System.out::println);

     }

 

     public static voidmain(String[]args) {

         testSorted();

     }

}

2)、运行效果

aa

bb

cc

dd

ee

2、演示定制排序

1)、代码实现

packagestreamAPI;

 

importjava.util.ArrayList;

importjava.util.Arrays;

importjava.util.List;

importjava.util.stream.Stream;

 

import 为什么要使用lambad表达式.entity.Employee;

 

public classTestStreamAPI4 {

    

     //创建数据

     private staticList<Employee>employees= Arrays.asList(

         newEmployee("张三",18,9999.99),

         newEmployee("李四",38,5555.55),

         newEmployee("王五",50,6666.66),

         newEmployee("赵六",16,3333.33),

         newEmployee("田七", 8,7777.77),

         newEmployee("田七", 8,7777.77)

     );

    

     /**

      *定制排序:按照姓名排序

      */

     public static voidtestSortes() {

         employees.stream().sorted((e1,e2)->-e1.getName().compareTo(e2.getName())).forEach(System.out::println);

     }

    

     public static voidmain(String[]args) {

         testSortes();

     }

}

2)、运行效果

Employee [name=赵六, age=16, salary=3333.33]

Employee [name=田七, age=8, salary=7777.77]

Employee [name=田七, age=8, salary=7777.77]

Employee [name=王五, age=50, salary=6666.66]

Employee [name=李四, age=38, salary=5555.55]

Employee [name=张三, age=18, salary=9999.99]

 

第六节:StreamApi之终止操作

一、stream的终止操作

终止操作,又称为终端操作,终端操作会从流的流水线生成结构,其结构可以是任何不是流的值,例如list、Integer,甚至是void;

我们常见的终端操作有查找、匹配等等,下面我们来看这些常用的方法函数:

二、查找与匹配

1、allMatch方法

该方法用于检测是否匹配所有元素;

2、anyMatch方法

该方法用于检测是否至少匹配一个元素;

3、noneMatch方法

该方法用于是否没有匹配所有元素;

4、findFirst方法

该方法用于返回的第一个元素;

5、findAny方法

该方法用于返回当前流中任意元素;

6、count方法

该方法用于返回流中元素的总个数;

7、max方法

该方法用于返回流中最大值;

8、min方法

该方法用于返回流中最小值;

 

三、实例演示

1、修改数据源

首先,为了演示,我们将这个数据源集合中的实体类再改造的稍微复杂一点点;

package 为什么要使用lambad表达式.entity;

 

public classEmployee {

 

    privateStringname;

   

    private int age;

   

    private double salary;

   

    privateStatusstatus;

 

    publicEmployee(Stringname,int age,double salary) {

        super();

        this.name=name;

        this.age=age;

        this.salary=salary;

    }

   

    publicEmployee(Stringname,int age,double salary,Statusstatus) {

        super();

        this.name=name;

        this.age=age;

        this.salary=salary;

        this.status=status;

    }

 

    publicEmployee() {

       

    }

   

    publicString getName() {

        return name;

    }

 

    public voidsetName(Stringname) {

        this.name=name;

    }

 

    public intgetAge() {

        return age;

    }

 

    public voidsetAge(int age) {

        this.age=age;

    }

 

    public doublegetSalary() {

        return salary;

    }

 

    public voidsetSalary(double salary) {

        this.salary=salary;

    }

 

    publicStatus getStatus() {

        return status;

    }

 

    public voidsetStatus(Statusstatus) {

        this.status=status;

    }

 

    @Override

    publicString toString() {

        return "Employee [name="+name+", age="+age+", salary="+salary+", status="+status+"]";

    }

 

    @Override

    public inthashCode() {

        final int prime= 31;

        int result= 1;

        result=prime*result+age;

        result=prime*result+ ((name==null) ? 0 :name.hashCode());

        long temp;

        temp= Double.doubleToLongBits(salary);

        result=prime*result+ (int) (temp^ (temp>>> 32));

        result=prime*result+ ((status==null) ? 0 :status.hashCode());

        return result;

    }

 

    @Override

    public booleanequals(Objectobj) {

        if(this==obj)

            return true;

        if(obj==null)

            return false;

        if(getClass() !=obj.getClass())

            return false;

        Employeeother= (Employee)obj;

        if(age!=other.age)

            return false;

        if(name==null) {

            if(other.name!=null)

                return false;

        }else if(!name.equals(other.name))

            return false;

        if(Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))

            return false;

        if(status!=other.status)

            return false;

        return true;

    }

 

    public enumStatus{

        FREE,//空闲

        BUSY,//繁忙

        VOCATION;//休假中

    }

   

}

2、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importjava.util.Optional;

 

import 为什么要使用lambad表达式.entity.Employee;

import 为什么要使用lambad表达式.entity.Employee.Status;

 

public classTestStreamAPI5 {

   

    //创建数据

    private staticList<Employee>employees= Arrays.asList(

        newEmployee("张三",18,9999.99,Status.FREE),

        newEmployee("李四",38,5555.55,Status.BUSY),

        newEmployee("王五",50,6666.66,Status.VOCATION),

        newEmployee("赵六",16,3333.33,Status.FREE),

        newEmployee("田七", 8,7777.77,Status.BUSY)

    );

   

    public static voidtestAllMatch() {

        //查看流中的元素的status字段值是否都是BUSY

        boolean b1=employees.stream().allMatch((e)->e.getStatus().equals(Status.BUSY));

        System.out.println("查看流中的元素的status字段值是否都是BUSY:"+b1);

       

        //查看流中的元素的status字段值是否至少有一个是BUSY

        boolean b2=employees.stream().anyMatch((e)->e.getStatus().equals(Status.BUSY));

        System.out.println("查看流中的元素的status字段值是否至少有一个是BUSY:"+b2);

       

        //查看流中的元素的status字段值是否全部都不是BUSY

        boolean b3=employees.stream().noneMatch((e)->e.getStatus().equals(Status.BUSY));

        System.out.println("查看流中的元素的status字段值是否全部都不是BUSY:"+b3);

       

        //按工资排序,然后获取第一个元素

        Optional<Employee>op1=employees.stream().

                                            sorted((e1,e2)->Double.compare(e1.getSalary(),e2.getSalary())).

                                            findFirst();

        System.out.println("按工资排序,然后获取第一个元素:"+op1.get());

       

        //返回当前流中任意元素:需求,随便给我找个闲人

        Optional<Employee>op2=employees.stream().filter((e)->e.getStatus().equals(Status.FREE)).findAny();

        System.out.println("返回当前流中任意元素:"+op2.get());

       

       

        //获取总数

        long count=employees.stream().count();

        System.out.println("获取总数:"+count);

       

        //获取最大值

        Optional<Employee>max=employees.stream().max((e1,e2)->Double.compare(e1.getSalary(),e2.getSalary()));

        System.out.println("工资最高的员工信息:"+max.get());

       

        //获取最小值

        Optional<Double>min=employees.stream().map(Employee::getSalary).min(Double::compare);

        System.out.println("工资最低的员工信息:"+min.get());

       

    }

   

    public static voidmain(String[]args) {

        testAllMatch();

    }

}

3、运行效果

查看流中的元素的status字段值是否都是BUSY:false

查看流中的元素的status字段值是否至少有一个是BUSY:true

查看流中的元素的status字段值是否全部都不是BUSY:false

按工资排序,然后获取第一个元素:Employee [name=赵六, age=16, salary=3333.33, status=FREE]

返回当前流中任意元素:Employee [name=张三, age=18, salary=9999.99, status=FREE]

获取总数:5

工资最高的员工信息:Employee [name=张三, age=18, salary=9999.99, status=FREE]

工资最低的员工信息:3333.33

 

第七节:StreamApi之归纳与搜集

接下来我们再看两种终止操作,分别是归纳与搜集;

一、归约

1、reduce(T identity,BinaryOperator)和reduce(BinaryOperator)方法

上述两个方法用于将流中元素反复结合起来,得到一个值;

reduce结合map使用,被称之为map-reduce模式,因为goole用它进行网络所搜而出名;

2、实例演示

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importjava.util.Optional;

 

import 为什么要使用lambad表达式.entity.Employee;

import 为什么要使用lambad表达式.entity.Employee.Status;

 

public classTestStreamAPI6 {

   

    //创建数据

    private staticList<Employee>employees= Arrays.asList(

        newEmployee("张三",18,9999.99,Status.FREE),

        newEmployee("李四",38,5555.55,Status.BUSY),

        newEmployee("王五",50,6666.66,Status.VOCATION),

        newEmployee("赵六",16,3333.33,Status.FREE),

        newEmployee("田七", 8,7777.77,Status.BUSY)

    );

   

    public static voidtest1() {

        List<Integer>list= Arrays.asList(1,2,3,4,5,6,7,8,9,10);

        Integersum=list.stream().reduce(0,(x,y)->x+y);

        System.out.println(sum);

    }

   

    public static voidtest2() {

        Optional<Double>resOp=employees.stream().map(Employee::getSalary).reduce(Double::sum);

        System.out.println("工资总数为:"+resOp.get());

    }

   

    public static voidmain(String[]args) {

        test1();

        test2();

    }

}

2)、运行效果

55

工资总数为:33333.3

 

二、搜集

1、collect方法

该方法用于将流转换为其他形式,接受一个collector接口的实现,用于给stream中元素做汇总的方法;

该方法需要一个参数,这个参数就是一个搜集器,即Collector接口中方法的实现决定了如何对流执行搜集操作,如搜集到list、set、map等等;但是collectors实现类提供了很多静态方法,可以方便的创建常见的搜集器实例,具体方法的实例演示如下:

2、collect方法实例演示

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.HashSet;

importjava.util.List;

importjava.util.Map;

importjava.util.Optional;

importjava.util.Set;

importjava.util.stream.Collectors;

 

importjavax.swing.plaf.synth.SynthSpinnerUI;

 

import 为什么要使用lambad表达式.entity.Employee;

import 为什么要使用lambad表达式.entity.Employee.Status;

 

public classTestStreamAPI6 {

    

     //创建数据

     private staticList<Employee>employees= Arrays.asList(

         newEmployee("张三",18,9999.99,Status.FREE),

         newEmployee("李四",38,5555.55,Status.BUSY),

         newEmployee("王五",50,6666.66,Status.VOCATION),

         newEmployee("赵六",16,3333.33,Status.FREE),

         newEmployee("田七", 8,7777.77,Status.BUSY)

     );

    

     public static voidtest1() {

         List<Integer>list= Arrays.asList(1,2,3,4,5,6,7,8,9,10);

         Integersum=list.stream().reduce(0,(x,y)->x+y);

         System.out.println(sum);

     }

    

    

     public static voidtest2() {

         Optional<Double>resOp=employees.stream().map(Employee::getSalary).reduce(Double::sum);

         System.out.println("工资总数为:"+resOp.get());

     }

    

     public static voidtest3() {

        

         //放到list集合中

         List<String>names1=employees.stream().

                                      map(Employee::getName).

                                      collect(/**搜集器**/Collectors.toList());

         System.out.println("list集合中:");

         names1.forEach(System.out::println);

        

         //放到set集合中

         Set<String>names2=employees.stream().

                   map(Employee::getName).

                   collect(Collectors.toSet());

         System.out.println("set集合中:");

         names2.forEach(System.out::println);

        

         //放到hashSet中

         HashSet<String>names3=employees.stream().

                   map(Employee::getName).

                   collect(Collectors.toCollection(HashSet::new));

         System.out.println("HashSet集合中:");

         names3.forEach(System.out::println);

        

         //获取平均值

         Doubledouble1=employees.stream().collect(Collectors.averagingDouble(Employee::getSalary));

         System.out.println("平均工资:"+double1);

        

         //获取总和

         Doubledouble2=employees.stream().collect(Collectors.summingDouble(Employee::getSalary));

         System.out.println("工资总和:"+double2);

        

         //获取最大值

         Optional<Employee>op=employees.stream().collect(Collectors.maxBy((e1,e2)->Double.compare(e1.getSalary(),e2.getSalary())));

         System.out.println("工资最高的人:"+op.get());

        

         //获取最少的工资数

         Optional<Double>double3=employees.stream().map(Employee::getSalary).collect(Collectors.minBy(Double::compare));

         System.out.println("获取最少的工资数:"+double3);

        

         //按状态分组

         Map<Status, List<Employee>>collectMap=employees.stream().collect(Collectors.groupingBy(Employee::getStatus));

         System.out.println(collectMap);

        

         //多级分组

         Map<Status, Map<String, List<Employee>>>resMap=employees.stream().collect(Collectors.groupingBy(Employee::getStatus,Collectors.groupingBy((e)->{

               if(((Employee)e).getAge()<=35) {

                    return "青年";

               }else if(((Employee)e).getAge()<=50) {

                    return "中年";

               }else{

                    return "老年";

               }

          })));

         System.out.println(resMap);

        

         //分区

         Map<Boolean, List<Employee>>resmap2=employees.stream().collect(Collectors.partitioningBy((e)->e.getSalary()>8000));

         System.out.println(resmap2);

        

     }

     public static voidmain(String[]args) {

//        test1();

//        test2();

         test3();

     }

}

2)、运行效果

list集合中:

张三

李四

王五

赵六

田七

set集合中:

李四

张三

王五

赵六

田七

HashSet集合中:

李四

张三

王五

赵六

田七

平均工资:6666.660000000001

工资总和:33333.3

工资最高的人:Employee [name=张三, age=18, salary=9999.99, status=FREE]

获取最少的工资数:Optional[3333.33]

{BUSY=[Employee [name=李四, age=38, salary=5555.55, status=BUSY], Employee [name=田七, age=8, salary=7777.77, status=BUSY]], VOCATION=[Employee [name=王五, age=50, salary=6666.66, status=VOCATION]], FREE=[Employee [name=张三, age=18, salary=9999.99, status=FREE], Employee [name=赵六, age=16, salary=3333.33, status=FREE]]}

{BUSY={青年=[Employee [name=田七, age=8, salary=7777.77, status=BUSY]], 中年=[Employee [name=李四, age=38, salary=5555.55, status=BUSY]]}, VOCATION={中年=[Employee [name=王五, age=50, salary=6666.66, status=VOCATION]]}, FREE={青年=[Employee [name=张三, age=18, salary=9999.99, status=FREE], Employee [name=赵六, age=16, salary=3333.33, status=FREE]]}}

{false=[Employee [name=李四, age=38, salary=5555.55, status=BUSY], Employee [name=王五, age=50, salary=6666.66, status=VOCATION], Employee [name=赵六, age=16, salary=3333.33, status=FREE], Employee [name=田七, age=8, salary=7777.77, status=BUSY]], true=[Employee [name=张三, age=18, salary=9999.99, status=FREE]]}

3)、其他

截图部分了解一下即可,没必要全看,API可以百度搜,我们主要是要掌握这个东西都能干嘛!

 

第八节:StreamApi之小小练习题

一、练习1

1、需求

给定一个数字列表[1,2,3,4,5...],返回一个由每个数的平方构成的列表[1,4,9,16,25...]

2、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

 

public classTestStreamAPI7 {

   

    public static voidtest1() {

        Integer[]nums=newInteger[] {1,2,3,4,5};

        Arrays.stream(nums).map((i)->{

            return i*i;

        }).forEach(System.out::println);;

    }

   

    public static voidmain(String[]args) {

        test1();

    }

}

3、运行效果

1

4

9

16

25

 

二、练习2

1、需求

怎样用map和reduce方法数一数流中有多少个对象;

2、实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importjava.util.Optional;

import 为什么要使用lambad表达式.entity.Employee;

 

public classTestStreamAPI7 {

     //创建数据

     private staticList<Employee>employees= Arrays.asList(

         newEmployee("张三",18,9999.99),

         newEmployee("李四",38,5555.55),

         newEmployee("王五",50,6666.66),

         newEmployee("赵六",16,3333.33),

         newEmployee("田七", 8,7777.77),

         newEmployee("田七", 8,7777.77)

     );

    

     public static voidtest2() {

         Optional<Integer>reduce=employees.stream().map((e)->1).reduce(Integer::sum);

         System.out.println(reduce.get());

     }   

     public static voidmain(String[]args) {

         test2();

     }

}

3、运行效果

6

 

三、练习3(综合题)

1、找出2011年发生的所有交易,并按交易额排序(从低到高)

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importstreamAPI.entity.Trader;

importstreamAPI.entity.Transaction;

 

public classTestStreamAPI8 {

   

    private staticTraderraoul=newTrader("Raoul","Cambridge");

    private staticTradermario=newTrader("Mario","Milan");

    private staticTraderalan=newTrader("Alan","Cambridge");

    private staticTraderbrian=newTrader("Brian","Cambridge");

   

    private staticList<Transaction>transactions= Arrays.asList(

            newTransaction(brian,2011,300),

            newTransaction(raoul,2012,1000),

            newTransaction(raoul,2011,400),

            newTransaction(mario,2012,710),

            newTransaction(mario,2012,700),

            newTransaction(alan,2012,950)

        ); 

   

    /**

     *找出2011年发生的所有交易,并按交易额排序(从低到高)

     */

    public static voidtest1() {

        transactions.stream().

            filter((e)->e.getYear()==2011).

            sorted((e1,e2)->Integer.

            compare(e1.getValue(),e2.getValue())).

            forEach(System.out::println);

    }

   

   

    public static voidmain(String[]args) {

        test1();

    }

}

2)、运行效果

Transaction [trader=Trader [name=Brian, city=Cambridge], year=2011, value=300]

Transaction [trader=Trader [name=Raoul, city=Cambridge], year=2011, value=400]

2、交易员都在哪些不同的城市工作过

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

 

importstreamAPI.entity.Trader;

importstreamAPI.entity.Transaction;

 

public classTestStreamAPI8 {

   

    private staticTraderraoul=newTrader("Raoul","Cambridge");

    private staticTradermario=newTrader("Mario","Milan");

    private staticTraderalan=newTrader("Alan","Cambridge");

    private staticTraderbrian=newTrader("Brian","Cambridge");

   

    private staticList<Transaction>transactions= Arrays.asList(

            newTransaction(brian,2011,300),

            newTransaction(raoul,2012,1000),

            newTransaction(raoul,2011,400),

            newTransaction(mario,2012,710),

            newTransaction(mario,2012,700),

            newTransaction(alan,2012,950)

    );

 

    /**

     *找出2011年发生的所有交易,并按交易额排序(从低到高)

     */

    public static voidtest2() {

        transactions.stream().

            map((t)->t.getTrader().getCity()).

            distinct().

            forEach(System.out::println);

    }

   

    public static voidmain(String[]args) {

        test2();

    }

}

2)、运行效果

Cambridge

Milan

3、查找所有来自剑桥的交易员,并按姓名排序

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

 

importstreamAPI.entity.Trader;

importstreamAPI.entity.Transaction;

 

public classTestStreamAPI8 {

    

     private staticTraderraoul=newTrader("Raoul","Cambridge");

     private staticTradermario=newTrader("Mario","Milan");

     private staticTraderalan=newTrader("Alan","Cambridge");

     private staticTraderbrian=newTrader("Brian","Cambridge");

    

     private staticList<Transaction>transactions= Arrays.asList(

              newTransaction(brian,2011,300),

              newTransaction(raoul,2012,1000), 

              newTransaction(raoul,2011,400),

              newTransaction(mario,2012,710),

              newTransaction(mario,2012,700),

              newTransaction(alan,2012,950)

     );

    

     /**

      *查找所有来自剑桥的交易员,并按姓名排序

      */

     public static voidtest3() {

         transactions.stream().

              filter((t)->t.getTrader().

              getCity().

              equals("Cambridge")).

              map((t)->t.getTrader()).

              sorted((t1,t2)->t1.getName().

              compareTo(t2.getName())).

              distinct().

              forEach(System.out::println);

     }

     public static voidmain(String[]args) {

         test3();

     }

}

2)、运行效果

Trader [name=Alan, city=Cambridge]

Trader [name=Brian, city=Cambridge]

Trader [name=Raoul, city=Cambridge]

4、返回所有交易员的姓名字符串,按字符顺序排序

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

 

importstreamAPI.entity.Trader;

importstreamAPI.entity.Transaction;

 

public classTestStreamAPI8 {

   

    private staticTraderraoul=newTrader("Raoul","Cambridge");

    private staticTradermario=newTrader("Mario","Milan");

    private staticTraderalan=newTrader("Alan","Cambridge");

    private staticTraderbrian=newTrader("Brian","Cambridge");

   

    private staticList<Transaction>transactions= Arrays.asList(

            newTransaction(brian,2011,300),

            newTransaction(raoul,2012,1000),

            newTransaction(raoul,2011,400),

            newTransaction(mario,2012,710),

            newTransaction(mario,2012,700),

            newTransaction(alan,2012,950)

    );

   

   

    /**

     *返回所有交易员的姓名字符串,按字符顺序排序

     */

    public static voidtest4() {

        transactions.stream().

            map((t)->t.getTrader().getName()).

            sorted().forEach(System.out::println);

    }

   

    public static voidmain(String[]args) {

        test4();

    }

}

2)、运行效果

Alan

Brian

Mario

Mario

Raoul

Raoul

 

5、有没有交易员是在米兰工作的

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

 

importstreamAPI.entity.Trader;

importstreamAPI.entity.Transaction;

 

public classTestStreamAPI8 {

   

    private staticTraderraoul=newTrader("Raoul","Cambridge");

    private staticTradermario=newTrader("Mario","Milan");

    private staticTraderalan=newTrader("Alan","Cambridge");

    private staticTraderbrian=newTrader("Brian","Cambridge");

   

    private staticList<Transaction>transactions= Arrays.asList(

            newTransaction(brian,2011,300),

            newTransaction(raoul,2012,1000),

            newTransaction(raoul,2011,400),

            newTransaction(mario,2012,710),

            newTransaction(mario,2012,700),

            newTransaction(alan,2012,950)

    );

   

    /**

     *有没有交易员是在米兰工作的

     */

    public static voidtest5() {

        boolean bool=transactions.stream().anyMatch((t)->t.getTrader().getCity().equals("Milan"));

        System.out.println(bool);

           

    }

   

    public static voidmain(String[]args) {

        test5();

    }

}

2)、运行效果

true

6、打印生活在剑桥的交易员的所有交易额

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importjava.util.Optional;

 

importstreamAPI.entity.Trader;

importstreamAPI.entity.Transaction;

 

public classTestStreamAPI8 {

   

    private staticTraderraoul=newTrader("Raoul","Cambridge");

    private staticTradermario=newTrader("Mario","Milan");

    private staticTraderalan=newTrader("Alan","Cambridge");

    private staticTraderbrian=newTrader("Brian","Cambridge");

   

    private staticList<Transaction>transactions= Arrays.asList(

            newTransaction(brian,2011,300),

            newTransaction(raoul,2012,1000),

            newTransaction(raoul,2011,400),

            newTransaction(mario,2012,710),

            newTransaction(mario,2012,700),

            newTransaction(alan,2012,950)

    );

    /**

     *打印生活在剑桥的交易员的所有交易额

     */

    public static voidtest6() {

        Optional<Integer>res=transactions.stream().

            filter((t)->t.getTrader().getCity().equals("Cambridge")).

            map(Transaction::getValue).

            reduce(Integer::sum);

        System.out.println(res.get());

    }

   

   

    public static voidmain(String[]args) {

        test6();

    }

}

2)、运行效果

2650

7、所有交易中,最高的交易额是多少

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importjava.util.Optional;

 

importstreamAPI.entity.Trader;

importstreamAPI.entity.Transaction;

 

public classTestStreamAPI8 {

   

    private staticTraderraoul=newTrader("Raoul","Cambridge");

    private staticTradermario=newTrader("Mario","Milan");

    private staticTraderalan=newTrader("Alan","Cambridge");

    private staticTraderbrian=newTrader("Brian","Cambridge");

   

    private staticList<Transaction>transactions= Arrays.asList(

            newTransaction(brian,2011,300),

            newTransaction(raoul,2012,1000),

            newTransaction(raoul,2011,400),

            newTransaction(mario,2012,710),

            newTransaction(mario,2012,700),

            newTransaction(alan,2012,950)

    );

   

    /**

     *所有交易中,最高的交易额是多少

     */

    public static voidtest7() {

        Optional<Integer>res=transactions.stream().

                map((t)->t.getValue()).

                max(Integer::compare);

        System.out.println(res.get());

           

    }

    public static voidmain(String[]args) {

        test7();

    }

}

2)、运行效果

1000

8、找到交易额最小的交易

1)、代码实现

packagestreamAPI;

 

importjava.util.Arrays;

importjava.util.List;

importjava.util.Optional;

 

importstreamAPI.entity.Trader;

importstreamAPI.entity.Transaction;

 

public classTestStreamAPI8 {

    

     private staticTraderraoul=newTrader("Raoul","Cambridge");

     private staticTradermario=newTrader("Mario","Milan");

     private staticTraderalan=newTrader("Alan","Cambridge");

     private staticTraderbrian=newTrader("Brian","Cambridge");

    

     private staticList<Transaction>transactions= Arrays.asList(

              newTransaction(brian,2011,300),

              newTransaction(raoul,2012,1000), 

              newTransaction(raoul,2011,400),

              newTransaction(mario,2012,710),

              newTransaction(mario,2012,700),

              newTransaction(alan,2012,950)

     );

     /**

      *找到交易额最小的交易

      */

     public static voidtest8() {

         Optional<Transaction>res=transactions.stream().

              min((t1,t2)->Integer.compare(t1.getValue(),t2.getValue()));

         System.out.println(res.get());

     }

    

     public static voidmain(String[]args) {

         test8();

     }

}

2)、运行效果

Transaction [trader=Trader [name=Brian, city=Cambridge], year=2011, value=300]

 

第九节:并行流与顺序流

一、并行流

1、并行流概念

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流;

Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。StreamApi可以声明性的通过parallel()与sequential()在并行流与串行流之间进行切换;

在学习并行流之前,我们要先了解一个框架,就是fork/join框架;

2、fork/join框架(原始的手段)

Fork/join框架就是在必要的情况下,将一个大任务,进行拆分fork成若干个小任务,拆到不可再拆时,再将一个个的小任务的运算结果进行join汇总,其原理图如下:

该框架的效率是很高的,他采用工作窃取模式(work-stealing):

当执行新的任务时,他可以将其拆分为更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把他放在自己的队列中。

相对于一般的线程池实现,fork/join框架的优势就体现在对其中包含的任务的处理方式上,在一般的线程池中,如果一个线程正在执行的任务由于某种原因无法继续运行,那么该线程会处于等待状态,而在foek/join框架实现中,

如果某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程就会主动寻找其他尚未运行的问题来执行,这种方式减少了线程的等待时间,提高了性能;

3、fork/join框架示例演示

1)、代码实现

packagestreamAPI;

 

importjava.util.concurrent.RecursiveTask;

 

public classForkJoinTestextendsRecursiveTask<Long> {

   

    private static final long serialVersionUID= 1L;

 

    private long start;

   

    private long end;

   

    publicForkJoinTest(long start,long end) {

        super();

        this.start=start;

        this.end=end;

    }

 

    private static final long THRESHOLD= 10000;

   

    @Override

    protectedLong compute() {

       

        long length=end-start;

       

        if(length<=THRESHOLD) {

            long sum= 0;

            for(long i=start;i<=end;i++) {

                sum+=i;

            }

            return sum;

        }else{

            long middle= (start+end)/2;

            ForkJoinTestleft=newForkJoinTest(start,middle);

            left.fork();//拆分子任务,同时压入线程队列

           

            ForkJoinTestright=newForkJoinTest(start,middle);

            right.fork();

           

            return left.join()+right.join();

        }

    }

}

 

packagestreamAPI;

 

importjava.time.Duration;

importjava.time.Instant;

importjava.util.concurrent.ForkJoinPool;

importjava.util.concurrent.ForkJoinTask;

 

public classForkJoinRun {

 

    public static voidmain(String[]args) {

        Instantstart= Instant.now();

        ForkJoinPoolpool=newForkJoinPool();

        ForkJoinTask<Long>task=newForkJoinTest(0,100000000L);

        Longsum=pool.invoke(task);

        System.out.println(sum);

        Instantend= Instant.now();

        System.out.println("耗费时间为:"+Duration.between(start,end).toMillis());

    }

}

2)、运行效果

305174216704

耗费时间为:79

3)、分析

这里就不和普通的for循环进行对比啦,数据量小的话,因为for循环走的是底层,他会更直接更快一点,数据量十分巨大的话,框架的优势就显示出来了,这个就像hadoop一样,数据小的话是不建议用的;

另外,我们可以看出来,他这个框架的用法其实很复杂的,所以在java8中就引入了一个全新的并行流,我们来看一下他到底有多简化;

4、java8并行流示例演示

1)、代码实现

packagestreamAPI;

importjava.time.Duration;

importjava.time.Instant;

importjava.util.concurrent.ForkJoinPool;

importjava.util.concurrent.ForkJoinTask;

importjava.util.stream.LongStream;

public classForkJoinRun {

    public static voidmain(String[]args) {

        test2();

    }

    public static voidtest2() {

        Instantstart= Instant.now();

        LongStream.rangeClosed(0,10000000000L).parallel().reduce(0,Long::sum);

        Instantend= Instant.now();

        System.out.println("耗费时间为:"+Duration.between(start,end).toMillis());

    }

}

2)、运行效果

耗费时间为:1707

3)、分析

从运行结果上看,他还快了点呢,这说明他底层封装的比我们自己写的好多了,哈哈,其实他底层封装的就是fork/join,但是你看现在这个封装后的结果,使用起来就非常的便捷了,

并且在并行与串行之间,还可以来回的切换,所以说,在处理大数据的时候,java8还是很给力的,你可以直接先并行,再map-reduce一下;

至于串行流,这里就不回顾了,前面几节课我们讲了半天的串行流啦;

 

 

 

 


版权声明:本文为hkkbbw原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。