目录
1、reduce(T identity,BinaryOperator)和reduce(BinaryOperator)方法
1、找出2011年发生的所有交易,并按交易额排序(从低到高)
第一节: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一下;
至于串行流,这里就不回顾了,前面几节课我们讲了半天的串行流啦;