目录
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、代码实现
我们在这里就一起演示这四种创建方式:
我们在这里就一起演示这四种创建方式:
package streamAPI;
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream;
import 为什么要使用lambad表达式.entity.Employee;
/** * 一、stream的三个操作步骤 * 1.创建stream * 2.中间操作 * 3.终止操作(终端操作) * * */ public class TestStreamAPI1 {
/** * 创建流 * 我们创建stream流有四种方式 * 如下: */ public static void testCreateStream() {
//1.通过collection系列集合提供的stream()方法获取串行流,或者通过parallelStream()方法获取并行流 List<String> list = new ArrayList<String>(); Stream<String> stream1 = list.stream();
//2.通过Arrays中的静态方法stream获取一个数组流 Employee[] emps = new Employee[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 void main(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)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List; import java.util.stream.Stream;
import 为什么要使用lambad表达式.entity.Employee;
public class TestStreamAPI2 {
//创建数据 private static List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99), new Employee("李四",38,5555.55), new Employee("王五",50,6666.66), new Employee("赵六",16,3333.33), new Employee("田七", 8,7777.77) );
public static void testFilter() {
//获取员工年龄大于等于35岁的员工信息 Stream<Employee> stream1 = employees.stream().filter((e)->e.getAge()>=35); //为了能看到效果,我们暂时先给他一个终止操作,后期会详细讲解终止操作 stream1.forEach(System.out::println); }
public static void main(String[] args) { testFilter(); } } |
2)、运行效果
Employee [name=李四, age=38, salary=5555.55] Employee [name=王五, age=50, salary=6666.66] |
2、实例演示limit(long maxSize)方法
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List; import java.util.stream.Stream;
import 为什么要使用lambad表达式.entity.Employee;
public class TestStreamAPI2 {
public static void testLimit() { //获取集合中员工工资大于5000的前两个员工信息 employees.stream(). filter((e)->e.getSalary()>5000). limit(2). forEach(System.out::println); } public static void main(String[] args) { testLimit(); } } |
2)、运行效果
Employee [name=张三, age=18, salary=9999.99] Employee [name=李四, age=38, salary=5555.55] |
3)、备注
Limit方法支持短路操作,一旦获取到满足条件的数据后,后面没有迭代的流,便不会再进行迭代了!这样的话,在某种场景下是可以提高效率的!和我们短路与&&和短路或||是一样的;
3、实例演示distinct()方法
1)、代码实现
package streamAPI; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import 为什么要使用lambad表达式.entity.Employee; public class TestStreamAPI2 { public static void testDistinct() { employees.stream(). distinct(). forEach(System.out::println); }
public static void main(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)、代码实现
package streamAPI; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import 为什么要使用lambad表达式.entity.Employee; public class TestStreamAPI2 { public static void testSkip() { //获取集合中员工工资大于5000的后两个员工信息 employees.stream(). filter((e)->e.getSalary()>5000). skip(2). forEach(System.out::println); } public static void main(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)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List;
import 为什么要使用lambad表达式.entity.Employee;
public class TestStreamAPI3 {
//创建数据 private static List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99), new Employee("李四",38,5555.55), new Employee("王五",50,6666.66), new Employee("赵六",16,3333.33), new Employee("田七", 8,7777.77), new Employee("田七", 8,7777.77) );
public static void testMap() { 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 void main(String[] args) { testMap(); }
} |
2)、运行效果
需求1:将给定集合中的数据专为大写 AA BB CC DD EE 需求2:提取员工姓名 张三 李四 王五 赵六 田七 田七 |
2、演示flatMap映射
首先,我们来看一下为什么要有flatMap这个操作;
我们先给定一个需求:将指定集合中的字符串拆分为字符,并打印输出之;
下面我们来看一下使用map操作的实现方式:
1)、代码实现
package streamAPI;
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream;
import 为什么要使用lambad表达式.entity.Employee;
public class TestStreamAPI3 {
//创建数据 private static List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99), new Employee("李四",38,5555.55), new Employee("王五",50,6666.66), new Employee("赵六",16,3333.33), new Employee("田七", 8,7777.77), new Employee("田七", 8,7777.77) );
public static void testMap2() { 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 * @param str * @return */ public static Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<Character>(); for(Character ch:str.toCharArray()) { list.add(ch); } return list.stream(); }
public static void main(String[] args) { testMap2(); } } |
2)、运行效果
a a b b c c d d e e |
3)、问题
可以看出来这个效果是没有问题的,但是在遍历的时候,是不是要遍历两次啊,这是不是很不符合我们代码的简洁约束啊,所以这个时候就需要使用flatMap操作来对其进行优化了;
下面我们来使用flatMap来演示:
1)、代码实现
package streamAPI; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; import 为什么要使用lambad表达式.entity.Employee; public class TestStreamAPI3 {
public static void testFlatMap() { 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 * @param str * @return */ public static Stream<Character> filterCharacter(String str){ List<Character> list = new ArrayList<Character>(); for(Character ch:str.toCharArray()) { list.add(ch); } return list.stream(); }
public static void main(String[] args) { testFlatMap(); } } |
2)、运行效果
a a b b c c d d e e |
第五节:StreamApi之排序
一、stream的排序概述
排序操作分为sorted自然排序和sortes(Compartor com)定制排序两种:
二、演示stream的排序操作
1、演示自然排序
1)、代码实现
package streamAPI;
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream;
import 为什么要使用lambad表达式.entity.Employee;
public class TestStreamAPI4 {
public static void testSorted() { List<String> list = Arrays.asList("aa","bb","cc","dd","ee"); list.stream().sorted().forEach(System.out::println); }
public static void main(String[] args) { testSorted(); } } |
2)、运行效果
aa bb cc dd ee |
2、演示定制排序
1)、代码实现
package streamAPI;
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream;
import 为什么要使用lambad表达式.entity.Employee;
public class TestStreamAPI4 {
//创建数据 private static List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99), new Employee("李四",38,5555.55), new Employee("王五",50,6666.66), new Employee("赵六",16,3333.33), new Employee("田七", 8,7777.77), new Employee("田七", 8,7777.77) );
/** * 定制排序:按照姓名排序 */ public static void testSortes() { employees.stream().sorted((e1,e2)->-e1.getName().compareTo(e2.getName())).forEach(System.out::println); }
public static void main(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 class Employee {
private String name;
private int age;
private double salary;
private Status status;
public Employee(String name, int age, double salary) { super(); this.name = name; this.age = age; this.salary = salary; }
public Employee(String name, int age, double salary,Status status) { super(); this.name = name; this.age = age; this.salary = salary; this.status = status; }
public Employee() {
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public double getSalary() { return salary; }
public void setSalary(double salary) { this.salary = salary; }
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
@Override public String toString() { return "Employee [name=" + name + ", age=" + age + ", salary=" + salary + ", status=" + status + "]"; }
@Override public int hashCode() { 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 boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Employee other = (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 enum Status{ FREE,//空闲 BUSY,//繁忙 VOCATION;//休假中 }
} |
2、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List; import java.util.Optional;
import 为什么要使用lambad表达式.entity.Employee; import 为什么要使用lambad表达式.entity.Employee.Status;
public class TestStreamAPI5 {
//创建数据 private static List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99,Status.FREE), new Employee("李四",38,5555.55,Status.BUSY), new Employee("王五",50,6666.66,Status.VOCATION), new Employee("赵六",16,3333.33,Status.FREE), new Employee("田七", 8,7777.77,Status.BUSY) );
public static void testAllMatch() { //查看流中的元素的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 void main(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)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List; import java.util.Optional;
import 为什么要使用lambad表达式.entity.Employee; import 为什么要使用lambad表达式.entity.Employee.Status;
public class TestStreamAPI6 {
//创建数据 private static List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99,Status.FREE), new Employee("李四",38,5555.55,Status.BUSY), new Employee("王五",50,6666.66,Status.VOCATION), new Employee("赵六",16,3333.33,Status.FREE), new Employee("田七", 8,7777.77,Status.BUSY) );
public static 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); }
public static void test2() { Optional<Double> resOp = employees.stream().map(Employee::getSalary).reduce(Double::sum); System.out.println("工资总数为:"+resOp.get()); }
public static void main(String[] args) { test1(); test2(); } } |
2)、运行效果
55 工资总数为:33333.3 |
二、搜集
1、collect方法
该方法用于将流转换为其他形式,接受一个collector接口的实现,用于给stream中元素做汇总的方法;
该方法需要一个参数,这个参数就是一个搜集器,即Collector接口中方法的实现决定了如何对流执行搜集操作,如搜集到list、set、map等等;但是collectors实现类提供了很多静态方法,可以方便的创建常见的搜集器实例,具体方法的实例演示如下:
2、collect方法实例演示
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors;
import javax.swing.plaf.synth.SynthSpinnerUI;
import 为什么要使用lambad表达式.entity.Employee; import 为什么要使用lambad表达式.entity.Employee.Status;
public class TestStreamAPI6 {
//创建数据 private static List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99,Status.FREE), new Employee("李四",38,5555.55,Status.BUSY), new Employee("王五",50,6666.66,Status.VOCATION), new Employee("赵六",16,3333.33,Status.FREE), new Employee("田七", 8,7777.77,Status.BUSY) );
public static 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); }
public static void test2() { Optional<Double> resOp = employees.stream().map(Employee::getSalary).reduce(Double::sum); System.out.println("工资总数为:"+resOp.get()); }
public static void test3() {
//放到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);
//获取平均值 Double double1 = employees.stream().collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println("平均工资:"+double1);
//获取总和 Double double2 = 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 void main(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、代码实现
package streamAPI;
import java.util.Arrays;
public class TestStreamAPI7 {
public static void test1() { Integer[] nums = new Integer[] {1,2,3,4,5}; Arrays.stream(nums).map((i)->{ return i*i; }).forEach(System.out::println);; }
public static void main(String[] args) { test1(); } } |
3、运行效果
1 4 9 16 25 |
二、练习2
1、需求
怎样用map和reduce方法数一数流中有多少个对象;
2、实现
package streamAPI;
import java.util.Arrays; import java.util.List; import java.util.Optional; import 为什么要使用lambad表达式.entity.Employee;
public class TestStreamAPI7 { //创建数据 private static List<Employee> employees = Arrays.asList( new Employee("张三",18,9999.99), new Employee("李四",38,5555.55), new Employee("王五",50,6666.66), new Employee("赵六",16,3333.33), new Employee("田七", 8,7777.77), new Employee("田七", 8,7777.77) );
public static void test2() { Optional<Integer> reduce = employees.stream().map((e)->1).reduce(Integer::sum); System.out.println(reduce.get()); } public static void main(String[] args) { test2(); } } |
3、运行效果
6 |
三、练习3(综合题)
1、找出2011年发生的所有交易,并按交易额排序(从低到高)
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List; import streamAPI.entity.Trader; import streamAPI.entity.Transaction;
public class TestStreamAPI8 {
private static Trader raoul = new Trader("Raoul","Cambridge"); private static Trader mario = new Trader("Mario","Milan"); private static Trader alan = new Trader("Alan","Cambridge"); private static Trader brian = new Trader("Brian","Cambridge");
private static List<Transaction> 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(alan,2012,950) );
/** * 找出2011年发生的所有交易,并按交易额排序(从低到高) */ public static void test1() { transactions.stream(). filter((e)->e.getYear()==2011). sorted((e1,e2)->Integer. compare(e1.getValue(),e2.getValue())). forEach(System.out::println); }
public static void main(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)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List;
import streamAPI.entity.Trader; import streamAPI.entity.Transaction;
public class TestStreamAPI8 {
private static Trader raoul = new Trader("Raoul","Cambridge"); private static Trader mario = new Trader("Mario","Milan"); private static Trader alan = new Trader("Alan","Cambridge"); private static Trader brian = new Trader("Brian","Cambridge");
private static List<Transaction> 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(alan,2012,950) );
/** * 找出2011年发生的所有交易,并按交易额排序(从低到高) */ public static void test2() { transactions.stream(). map((t)->t.getTrader().getCity()). distinct(). forEach(System.out::println); }
public static void main(String[] args) { test2(); } } |
2)、运行效果
Cambridge Milan |
3、查找所有来自剑桥的交易员,并按姓名排序
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List;
import streamAPI.entity.Trader; import streamAPI.entity.Transaction;
public class TestStreamAPI8 {
private static Trader raoul = new Trader("Raoul","Cambridge"); private static Trader mario = new Trader("Mario","Milan"); private static Trader alan = new Trader("Alan","Cambridge"); private static Trader brian = new Trader("Brian","Cambridge");
private static List<Transaction> 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(alan,2012,950) );
/** * 查找所有来自剑桥的交易员,并按姓名排序 */ public static void test3() { 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 void main(String[] args) { test3(); } } |
2)、运行效果
Trader [name=Alan, city=Cambridge] Trader [name=Brian, city=Cambridge] Trader [name=Raoul, city=Cambridge] |
4、返回所有交易员的姓名字符串,按字符顺序排序
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List;
import streamAPI.entity.Trader; import streamAPI.entity.Transaction;
public class TestStreamAPI8 {
private static Trader raoul = new Trader("Raoul","Cambridge"); private static Trader mario = new Trader("Mario","Milan"); private static Trader alan = new Trader("Alan","Cambridge"); private static Trader brian = new Trader("Brian","Cambridge");
private static List<Transaction> 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(alan,2012,950) );
/** * 返回所有交易员的姓名字符串,按字符顺序排序 */ public static void test4() { transactions.stream(). map((t)->t.getTrader().getName()). sorted().forEach(System.out::println); }
public static void main(String[] args) { test4(); } } |
2)、运行效果
Alan Brian Mario Mario Raoul Raoul
|
5、有没有交易员是在米兰工作的
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List;
import streamAPI.entity.Trader; import streamAPI.entity.Transaction;
public class TestStreamAPI8 {
private static Trader raoul = new Trader("Raoul","Cambridge"); private static Trader mario = new Trader("Mario","Milan"); private static Trader alan = new Trader("Alan","Cambridge"); private static Trader brian = new Trader("Brian","Cambridge");
private static List<Transaction> 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(alan,2012,950) );
/** * 有没有交易员是在米兰工作的 */ public static void test5() { boolean bool = transactions.stream().anyMatch((t)->t.getTrader().getCity().equals("Milan")); System.out.println(bool);
}
public static void main(String[] args) { test5(); } } |
2)、运行效果
true |
6、打印生活在剑桥的交易员的所有交易额
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List; import java.util.Optional;
import streamAPI.entity.Trader; import streamAPI.entity.Transaction;
public class TestStreamAPI8 {
private static Trader raoul = new Trader("Raoul","Cambridge"); private static Trader mario = new Trader("Mario","Milan"); private static Trader alan = new Trader("Alan","Cambridge"); private static Trader brian = new Trader("Brian","Cambridge");
private static List<Transaction> 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(alan,2012,950) ); /** * 打印生活在剑桥的交易员的所有交易额 */ public static void test6() { 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 void main(String[] args) { test6(); } } |
2)、运行效果
2650 |
7、所有交易中,最高的交易额是多少
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List; import java.util.Optional;
import streamAPI.entity.Trader; import streamAPI.entity.Transaction;
public class TestStreamAPI8 {
private static Trader raoul = new Trader("Raoul","Cambridge"); private static Trader mario = new Trader("Mario","Milan"); private static Trader alan = new Trader("Alan","Cambridge"); private static Trader brian = new Trader("Brian","Cambridge");
private static List<Transaction> 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(alan,2012,950) );
/** * 所有交易中,最高的交易额是多少 */ public static void test7() { Optional<Integer> res = transactions.stream(). map((t)->t.getValue()). max(Integer::compare); System.out.println(res.get());
} public static void main(String[] args) { test7(); } } |
2)、运行效果
1000 |
8、找到交易额最小的交易
1)、代码实现
package streamAPI;
import java.util.Arrays; import java.util.List; import java.util.Optional;
import streamAPI.entity.Trader; import streamAPI.entity.Transaction;
public class TestStreamAPI8 {
private static Trader raoul = new Trader("Raoul","Cambridge"); private static Trader mario = new Trader("Mario","Milan"); private static Trader alan = new Trader("Alan","Cambridge"); private static Trader brian = new Trader("Brian","Cambridge");
private static List<Transaction> 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(alan,2012,950) ); /** * 找到交易额最小的交易 */ public static void test8() { Optional<Transaction> res = transactions.stream(). min((t1,t2)->Integer.compare(t1.getValue(),t2.getValue())); System.out.println(res.get()); }
public static void main(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)、代码实现
package streamAPI;
import java.util.concurrent.RecursiveTask;
public class ForkJoinTest extends RecursiveTask<Long> {
private static final long serialVersionUID = 1L;
private long start;
private long end;
public ForkJoinTest(long start, long end) { super(); this.start = start; this.end = end; }
private static final long THRESHOLD = 10000;
@Override protected Long 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; ForkJoinTest left = new ForkJoinTest(start,middle); left.fork();//拆分子任务,同时压入线程队列
ForkJoinTest right = new ForkJoinTest(start,middle); right.fork();
return left.join()+right.join(); } } } |
package streamAPI;
import java.time.Duration; import java.time.Instant; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask;
public class ForkJoinRun {
public static void main(String[] args) { Instant start = Instant.now(); ForkJoinPool pool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinTest(0,100000000L); Long sum = pool.invoke(task); System.out.println(sum); Instant end = Instant.now(); System.out.println("耗费时间为:"+Duration.between(start, end).toMillis()); } } |
2)、运行效果
305174216704 耗费时间为:79 |
3)、分析
这里就不和普通的for循环进行对比啦,数据量小的话,因为for循环走的是底层,他会更直接更快一点,数据量十分巨大的话,框架的优势就显示出来了,这个就像hadoop一样,数据小的话是不建议用的;
另外,我们可以看出来,他这个框架的用法其实很复杂的,所以在java8中就引入了一个全新的并行流,我们来看一下他到底有多简化;
4、java8并行流示例演示
1)、代码实现
package streamAPI; import java.time.Duration; import java.time.Instant; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.stream.LongStream; public class ForkJoinRun { public static void main(String[] args) { test2(); } public static void test2() { Instant start = Instant.now(); LongStream.rangeClosed(0,10000000000L).parallel().reduce(0,Long::sum); Instant end = Instant.now(); System.out.println("耗费时间为:"+Duration.between(start, end).toMillis()); } } |
2)、运行效果
耗费时间为:1707 |
3)、分析
从运行结果上看,他还快了点呢,这说明他底层封装的比我们自己写的好多了,哈哈,其实他底层封装的就是fork/join,但是你看现在这个封装后的结果,使用起来就非常的便捷了,
并且在并行与串行之间,还可以来回的切换,所以说,在处理大数据的时候,java8还是很给力的,你可以直接先并行,再map-reduce一下;
至于串行流,这里就不回顾了,前面几节课我们讲了半天的串行流啦;