Lamdba 1、为什么使用Lamdba? Lamdba是一个匿名函数,我们可以把Lamdba表达式理解为一段可以传递的代码,这样我们就能够写出更简洁更灵活的代码,也提升了Java的表达能力
2、Lamdba的实际应用场景
假设现有需求如下:查询出公司中年龄大于35的员工信息
给定的信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Employee { private String name; private int age; private double salary; public Employee (String name, int age, double salary) { this .name = name; this .age = age; this .salary = salary; } } Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("李四" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("赵六" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5);
在没有学习Lamdba之前,我们可以定义方法来实现这个需求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public List<Employee> filterEmployee (List<Employee> emps) { List<Employee> results = new ArrayList<>(); for (Employee emp : emps) { if (emp.getAge() > 35 ) { results.add(emp); } } return results; } --------------------------------------------------------------------------- @Test public void test01 () { List<Employee> list = filterEmployee(emps); for (Employee emp : list) { System.out.println(emp); } } ============控制台信息============ name=李四, age=44 , salary=4444.44 name=王五, age=55 , salary=5555.55 name=赵六, age=66 , salary=6666.66 name=田七, age=77 , salary=7777.77
如果这个时候添加了一个新需求,需要查询工资低于5000的员工信息
1 2 3 4 5 6 7 8 9 10 11 12 13 public List<Employee> filterEmployee2 (List<Employee> emps) { List<Employee> results = new ArrayList<>(); for (Employee emp : emps) { if (emp.getSalary() < 5000 ) { results.add(emp); } } return results; } ============控制台信息============ name=张三, age=33 , salary=3333.33 name=李四, age=44 , salary=4444.44
这个时候问题就来了,在我们编写的这段代码中,真正不一样的地方只有一行,但是我们却写了一个新方法来实现现有的需求变动,代码的复用性与扩展性都很差。如果将来有更多的需求,那么我们需要重写很多方法来满足需求的变动,这个时候我们可以考虑使用设计模式来进行修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public interface MyPredicate <T > { public boolean test (T t) ; } --------------------------------------------------------------------------- public class FilterEmployeeByAge implements MyPredicate <Employee > { @Override public boolean test (Employee emp) { return emp.getAge() > 35 ; } } --------------------------------------------------------------------------- public class FilterEmployeeBySalary implements MyPredicate <Employee > { @Override public boolean test (Employee emp) { return emp.getSalary() < 5000 ; } } --------------------------------------------------------------------------- public List<Employee> filterEmployee (List<Employee> emps, MyPredicate<Employee> mp) { List<Employee> results = new ArrayList<>(); for (Employee emp : emps) { if (mp.test(emp)) { results.add(emp); } } return results; } --------------------------------------------------------------------------- List<Employee> listByAge = filterEmployee(emps, new FilterEmployeeByAge()); List<Employee> listBySalary = filterEmployee(emps, new FilterEmployeeBySalary());
通过设计模式的改进,代码确实可扩展性比之前好了许多,但是又引发了新的问题。我们每次都要为了实现新的逻辑编写一个实现类文件,例如:FilterEmployeeBySalary和FilterEmployeeByAge,这个时候我们很自然的想到可以使用匿名内部类来解决这个问题,使我们的项目结构看起来更整洁
1 2 3 4 5 6 7 List<Employee> listByAge = filterEmployee(emps, new MyPredicate<Employee>() { @Override public boolean test (Employee emp) { return emp.getAge() > 35 ; } });
通过观察匿名内部类我们发现,真正的校验逻辑其实只有emp.getAge() > 35 这一句,有没有办法再次进行优化呢?那么我们终于可以引入今天的主角Lamdba啦
1 2 List<Employee> listByAge = filterEmployee(emps, e -> e.getAge() > 35 );
通过上面的这个例子,我们已经感受到了Lamdba给我们带来的方便,那么下面我们就开始正式学习Lamdba的语法吧~
3、Lamdba基本语法 Java8中引入了一个新的操作符 **”->”**,该操作符被称为箭头操作符
该操作符将Lamdba表达式分成了两部分
左侧:Lamdba表达式的参数列表
右侧:Lamdba表达式中需要执行的功能
常见的语法格式有如下几种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Runnable run = () -> System.out.println("Hello Lamdba" ); run.run(); Consumer<String> con = (s) -> System.out.println(s); Consumer<String> con = s -> System.out.println(s); con.accept("Hello Lamdba" ); Comparator<Integer> com = (x, y) -> { System.out.println("Hello Lamdba" ); return Integer.compare(x, y); }; Comparator<Integer> com = (x, y) -> Integer.compare(x, y); Comparator<Integer> com = (Integer x, Integer y) -> Integer.compare(x, y);
4、使用的条件 Lamdba表达式需要函数式接口的支持
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口
通常我们可以使用注解@FunctionalInterface来进行检验
1 2 3 4 5 @FunctionalInterface public interface MyPredicate <T > { boolean test (T t) ; }
5、内置核心函数式接口
接口名
接口描述
内置方法
Consumer
消费型接口
void accept(T t)
Supplier
供给型接口
T get()
Function<T,R>
函数型接口
R apply(T t)
Predicate
断言型接口
boolean test(T t)
下面我们通过例子来演示一下
需求:传入任意字符串进行打印
分析:符合需要传入一个参数 且无需返回值的情况
选择:Consumer接口
1 2 3 4 5 6 7 8 @Test public void test () { Consumer<String> con = s -> System.out.println(s); Consumer<String> con = System.out::println; con.accept("Hello Lamdba" ); }
需求:获取当前的日期
分析:符合无需传入参数 且需要返回一个值
选择:Supplier接口
1 2 3 4 5 6 7 8 @Test public void test () { Supplier<Date> supplier = () -> new Date(); Supplier<Date> supplier = Date::new ; Date today = supplier.get(); }
需求:获取任意日期的字符串表格式
分析:需要传入一个Date类型 返回一个String类型
选择:Function接口
1 2 3 4 5 6 7 8 @Test public void test () { Function<Date, String> con = d -> new SimpleDateFormat("yyyyMM" ).format(d); Function<Date, String> con = new SimpleDateFormat("yyyyMM" )::format; String dateStr = con.apply(new Date()); }
需求:判断一个字符串是否包含Lamdba
分析:传入一个值 返回一个boolean
选择:Predicate接口
1 2 3 4 5 6 7 8 9 @Test public void test () { Predicate<String> pre = s -> s.contains("Lamdba" ); boolean test = pre.test("Hello Lamdba" ); Predicate<String> pre = "Hello Lamdba" ::contains; boolean test = pre.test("Lamdba" ); }
6、方法引用与构造器 若Lamdba体中的内容有方法已经实现了,我们可以使用方法引用,也可以理解为方法引用是Lamdba的另一种表现形式
主要有三种语法格式:
对象 : : 实例方法名
类名 : : 静态方法名
类名 : : 实例方法名
注意:
Lamdba体中调用方法的参数列表与返回值类型,要与函数式接口中的类型一致
若Lamdba参数列表中的第一参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName :: method
具体例子可看上方两种实现方式
构造器引用
格式:ClassName : : new
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的列表保持一致
构造参数
函数式接口参数
空参构造
Supplier刚好符合不需要参数,返回一个值
一个参数构造
Function刚好符合传入一个参数,返回一个值
两个参数构造
BiFunction刚好符合传入两个参数,返回一个值
Stream API 1、Stream的三个操作步骤
创建Stream
中间操作
终止操作
创建Stream的四种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void test () { List<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); Employee[] emps = new Employee[10 ]; Stream<Employee> stream2 = Arrays.stream(emps); Stream<String> stream3 = Stream.of("aa" , "bb" , "cc" ); Stream<Integer> stream4 = Stream.iterate(0 , (x) -> x + 2 ); stream4.limit(10 ).forEach(System.out::println); Stream.generate(() -> Math.random()).limit(5 ).forEach(System.out::println); }
2、筛选与切片
filter——接收Lamdba,从流中排除某些元素
1 2 3 4 5 Stream.of("aa" , "bb" , "cc" ).filter(x -> x.equals("bb" )) .forEach(System.out::println); ============控制台信息============ bb
limit——截断流,使其元素不超过给定数量
1 2 3 4 5 Stream.of("aa" , "bb" , "cc" ).limit(2 ).forEach(System.out::println); ============控制台信息============ aa bb
skip(n)——跳过元素,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流,与limit(n)互补
1 2 3 4 5 6 7 8 9 Stream.of("aa" , "bb" , "cc" ).skip(2 ).forEach(System.out::println); ============控制台信息============ cc Stream.of("aa" , "bb" , "cc" ).skip(3 ).forEach(System.out::println); ============控制台信息============ 此处为空的
distinct——筛选,通过流所生成元素的hashCode和equals方法去除重复元素
1 2 3 4 5 6 7 8 9 Stream.of("aa" , "bb" , "bb" , "cc" , "cc" ) .distinct() .forEach(System.out::println); ============控制台信息============ aa bb cc
下面我们综合使用以下上面学到的五种
1 2 3 4 5 6 7 8 9 10 Stream.of("ab" , "bc" , "bc" , "cd" , "de" , "abcde" ) .distinct() .filter(x -> x.contains("b" )) .skip(1 ) .limit(2 ) .forEach(System.out::println); ============控制台信息============ bc abcde
map——映射,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
注意:使用map后返回的是Stream流,T为函数的返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("李四" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("赵六" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); emps.stream().map(Employee::getName).forEach(System.out::println); ============控制台信息============ 张三 李四 王五 赵六 田七
flatMap——映射,接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流,简言之,flatMap中的函数返回值需要是一个流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 List<String> list = Arrays.asList("aaa" , "bbb" ); list.stream().flatMap(s -> { List<Character> rList = new ArrayList<>(); for (Character c : s.toCharArray()) { rList.add(c); } return rList.stream(); }).forEach(System.out::println); ============控制台信息============ a a a b b b
sorted——自然排序(Comparable)
1 2 3 4 5 6 7 8 9 10 List<String> list = Arrays.asList("ccc" , "aaa" , "bbb" , "ddd" , "eee" ); list.stream().sorted().forEach(System.out::println); ============控制台信息============ aaa bbb ccc ddd eee
sorted(Comparator com)——定制排序(Comparator)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Employee emp1 = new Employee("张三" , 33 , 777 ); Employee emp2 = new Employee("李四" , 33 , 666 ); Employee emp3 = new Employee("王五" , 55 , 555 ); Employee emp4 = new Employee("赵六" , 55 , 444 ); Employee emp5 = new Employee("田七" , 55 , 333 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); emps.stream().sorted((s1, s2) -> { if (s1.getAge() == s2.getAge()) { return Double.compare(s1.getSalary(), s2.getSalary()); } else { return s1.getAge() - s2.getAge(); } }).forEach(System.out::println); ============控制台信息============ Employee [name=李四, age=33 , salary=666.0 ] Employee [name=张三, age=33 , salary=777.0 ] Employee [name=田七, age=55 , salary=333.0 ] Employee [name=赵六, age=55 , salary=444.0 ] Employee [name=王五, age=55 , salary=555.0 ]
allMatch——检查是否匹配所有元素
1 2 3 4 5 List<Integer> list = Arrays.asList(111 , 222 , 333 , 444 , 555 ); boolean flag = list.stream().allMatch(s -> s > 200 );System.out.println("是否都大于200:" + flag); ============控制台信息============ 是否都大于200 :false
anyMatch——检查是否至少匹配一个元素
1 2 3 4 5 List<Integer> list = Arrays.asList(111 , 222 , 333 , 444 , 555 ); boolean flag = list.stream().anyMatch(s -> s > 200 );System.out.println("是否有值大于200:" + flag); ============控制台信息============ 是否有值大于200 :true
noneMatch——检查是否没有匹配所有元素
1 2 3 4 5 List<Integer> list = Arrays.asList(111 , 222 , 333 , 444 , 555 ); boolean flag = list.stream().noneMatch(s -> s > 200 );System.out.println("是否没有值大于200:" + flag); ============控制台信息============ 是否没有值大于200 :false
findFirst——返回第一个元素
1 2 3 4 5 List<Integer> list = Arrays.asList(111 , 222 , 333 , 444 , 555 ); Optional<Integer> first = list.stream().findFirst(); System.out.println("元素中的第一个值是:" +first.get()); ============控制台信息============ 元素中的第一个值是:111
findAny——返回当前流中的任意元素
1 2 3 4 5 6 7 8 9 List<Integer> list = Arrays.asList(111 , 222 , 333 , 444 , 555 ); Optional<Integer> any = list.parallelStream().findAny(); System.out.println("元素中任意元素是:" +any.get()); ============控制台信息============ 元素中任意元素是:333
count——返回流中元素的总个数
1 2 3 4 5 List<Integer> list = Arrays.asList(111 , 222 , 333 , 444 , 555 ); long count = list.stream().count();System.out.println(count); ============控制台信息============ 5
max——返回流中元素的最大值
1 2 3 4 5 List<Integer> list = Arrays.asList(111 , 222 , 555 , 444 , 333 ); Optional<Integer> max = list.stream().max(Integer::compareTo); System.out.println(max.get()); ============控制台信息============ 555
min——返回流中元素的最小值
1 2 3 4 5 List<Integer> list = Arrays.asList(111 , 222 , 555 , 444 , 333 ); Optional<Integer> min = list.stream().min(Integer::compareTo); System.out.println(min.get()); ============控制台信息============ 111
reduce(T identity,BinaryOperator) / reduce(BinaryOperator)——可以将流中的元素反复结合起来,得到一个值
注意:如果第一个参数未指定起始值 那么返回值类型就为Optional 否则返回元素类型
1 2 3 4 5 6 7 List<Integer> list = Arrays.asList(1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ); Integer sum = list.stream().reduce(0 , (x, y) -> x + y); System.out.println(sum); ============控制台信息============ 55
collect——将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总方法
收集数据到List:Collectors.toList()
1 2 3 4 5 6 7 8 9 10 11 12 13 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("李四" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("赵六" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); List<String> names = emps.stream().map(Employee::getName).collect(Collectors.toList()); System.out.println(names); ============控制台信息============ [张三, 李四, 王五, 赵六, 田七]
收集数据到Set:Collectors.toSet()
1 2 3 4 5 6 7 8 9 10 11 12 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); Set<String> names = emps.stream().map(Employee::getName).collect(Collectors.toSet()); System.out.println(names); ============控制台信息============ [张三, 王五, 田七]
收集到HashSet等特殊集合中:Collectors.toCollection(HashSet::new)
1 2 3 4 5 6 7 8 9 10 11 12 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); HashSet<String> names = emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new )); System.out.println(names); ============控制台信息============ [张三, 王五, 田七]
收集总数:Collectors.counting()
1 2 3 4 5 6 7 8 9 10 11 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); Long count = emps.stream().collect(Collectors.counting()); System.out.println(count); ============控制台信息============ 5
计算工资的平均值:Collectors.averagingDouble()
、Collectors.averagingInt()
、Collectors.averagingLong()
,示例中用Double
进行演示
1 2 3 4 5 6 7 8 9 10 11 12 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avg); ============控制台信息============ 5555.55
计算工资的总和:Collectors.summingDouble()
、Collectors.summingInt()
、Collectors.summingLong()
,示例中用Double
进行演示
1 2 3 4 5 6 7 8 9 10 11 12 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); Double sum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary)); System.out.println(sum); ============控制台信息============ 27777.75
获取工资最大/最小的员工信息:Collectors.maxBy()
/Collectors.minBy()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); Optional<Employee> max = emps.stream().collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))); Optional<Employee> min = emps.stream().collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))); System.out.println(max.get()); System.out.println(min.get()); ============控制台信息============ Employee [name=田七, age=77 , salary=7777.77 ] Employee [name=张三, age=33 , salary=3333.33 ]
根据名字进行分组:Collectors.groupingBy()
1 2 3 4 5 6 7 8 9 10 11 12 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); Map<String, List<Employee>> group = emps.stream().collect(Collectors.groupingBy(Employee::getName)); System.out.println(group); ============控制台信息============ {张三=[Employee [name=张三, age=33 , salary=3333.33 ], Employee [name=张三, age=44 , salary=4444.44 ]], 王五=[Employee [name=王五, age=55 , salary=5555.55 ], Employee [name=王五, age=66 , salary=6666.66 ]], 田七=[Employee [name=田七, age=77 , salary=7777.77 ]]}
根据条件进行分区:Collectors.partitioningBy()
1 2 3 4 5 6 7 8 9 10 11 12 13 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); Map<Boolean, List<Employee>> map = emps.stream().collect(Collectors.partitioningBy(e->e.getSalary()>7000 )); System.out.println(map); ============控制台信息============ {false =[Employee [name=张三, age=33 , salary=3333.33 ], Employee [name=张三, age=44 , salary=4444.44 ], Employee [name=王五, age=55 , salary=5555.55 ], Employee [name=王五, age=66 , salary=6666.66 ]], true =[Employee [name=田七, age=77 , salary=7777.77 ]]}
按照指定分隔符拼接字符串:Collectors.joining()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("张三" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("王五" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); String s1 = emps.stream().map(Employee::getName).collect(Collectors.joining()); String s2 = emps.stream().map(Employee::getName).collect(Collectors.joining("," )); String s3 = emps.stream().map(Employee::getName).collect(Collectors.joining("," , "开始[" , "]结束 " )); System.out.println(s1); System.out.println(s2); System.out.println(s3); ============控制台信息============ 张三张三王五王五田七 张三,张三,王五,王五,田七 开始[张三,张三,王五,王五,田七]结束
3、Stream练习题
1、给定一个数字列表,如何返回一个由每个数的平方构成的列表呢?
例如:给定【1,2,3,4,5】,应该返回【1,4,9,16,25】
1 2 3 4 5 List<Integer> nums = Arrays.asList(1 , 2 , 3 , 4 , 5 ); List<Integer> collect = nums.stream().map(e -> e * e).collect(Collectors.toList()); System.out.println(collect); ============控制台信息============ [1 , 4 , 9 , 16 , 25 ]
2、怎样用map和reduce方法数一数流中由多少个Employee呢?
1 2 3 4 5 6 7 8 9 10 Employee emp1 = new Employee("张三" , 33 , 3333.33 ); Employee emp2 = new Employee("李四" , 44 , 4444.44 ); Employee emp3 = new Employee("王五" , 55 , 5555.55 ); Employee emp4 = new Employee("赵六" , 66 , 6666.66 ); Employee emp5 = new Employee("田七" , 77 , 7777.77 ); List<Employee> emps = Arrays.asList(emp1, emp2, emp3, emp4, emp5); Integer sum = emps.stream().map(e -> 1 ).reduce(0 , Integer::sum); System.out.println(sum); ============控制台信息============ 5
并行流与顺序流 什么是ForkJoin
Fork/Join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行join汇总
Fork/Join与传统线程池的区别
采用工作窃取
模式(work-stealing):
当执行新的任务时,它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上,在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态,而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行,这种方式减少了线程的等待时间,提高了性能
计算0~10000000000L的总和
传统方式使用Fork/Join
计算,需要编写一个类继承RecursiveTask<T>
或RecursiveAction
,两种的区别是前者有返回值,后者无返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import java.util.concurrent.RecursiveTask;public class ForkJoinCalculate extends RecursiveTask <Long > { private static final long serialVersionUID = 964117657719938939L ; private long start; private long end; private static final long THRESHOLD = 10000 ; public ForkJoinCalculate (long start, long end) { this .start = start; this .end = end; } @Override protected Long compute () { long length = end - start; if (length > THRESHOLD) { long middle = (start + end) / 2 ; ForkJoinCalculate left = new ForkJoinCalculate(start, middle); left.fork(); ForkJoinCalculate right = new ForkJoinCalculate(middle + 1 , end); right.fork(); return left.join() + right.join(); } else { long sum = 0 ; for (long i = start; i < end; i++) { sum += i; } return sum; } } }
之后我们编写测试类进行调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Instant start = Instant.now(); ForkJoinPool pool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinCalculate(0 , 10000000000L ); Long sum = pool.invoke(task); Instant end = Instant.now(); System.out.println("sum:" + sum); System.out.println("耗费时间:" + Duration.between(start, end).toMillis()); ============控制台信息============ sum:-5345475101129947011 耗费时间:2054
需要注意的是,ForkJoin拆分任务也是需要耗费时间的,所以一般只建议在大数据量下使用该方式进行调用,且具体的执行速度也受限于具体的拆分逻辑代码
同时我们也不难发现,这种方式编写起来较为复杂,使用起来不是太方便
那么JAVA8中我们如何简化ForkJoin的调用呢?
1 2 3 4 5 6 7 8 9 10 Instant start = Instant.now(); long sum = LongStream.range(0 , 10000000000L ).parallel().reduce(0 , Long::sum);Instant end = Instant.now(); System.out.println("sum:" + sum); System.out.println("耗费时间:" + Duration.between(start, end).toMillis()); ============控制台信息============ sum:-5340232226128654848 耗费时间:1343