JDK8中的新特性(Lambda、函数式接口、方法引用、Stream)(一):https://developer.aliyun.com/article/1416365
练习5:判断型接口
代码示例:Predicate<T>接口
JDK1.8时,Collecton<E>接口增加了一下方法,其中一个如下:
public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足filter指定的条件判断的。
public default void forEach(Consumer<? super T> action) 遍历Collection集合的每个元素,执行“xxx消费型”操作。
案例:
(1)添加一些字符串到一个Collection集合中
(2)调用forEach遍历集合
(3)调用removeIf方法,删除其中字符串的长度<5的
(4)再次调用forEach遍历集合
import java.util.ArrayList; public class TestLambda { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("hello"); list.add("java"); list.add("ok"); list.add("yes"); list.forEach(str->System.out.println(str)); System.out.println(); list.removeIf(str->str.length()<5); list.forEach(str->System.out.println(str)); } }
练习6:判断型接口
案例:
(1)声明一个Employee员工类型,包含编号、姓名、性别,年龄,薪资。
(2)声明一个EmployeeSerice员工管理类,包含一个ArrayList集合的属性all,在EmployeeSerice的构造器中,创建一些员工对象,为all集合初始化。
(3)在EmployeeSerice员工管理类中,声明一个方法:ArrayList get(Predicate p),即将满足p指定的条件的员工,添加到一个新的ArrayList 集合中返回。
(4)在测试类中创建EmployeeSerice员工管理类的对象,并调用get方法,分别获取:
- 所有员工对象
- 所有年龄超过35的员工
- 所有薪资高于15000的女员工
- 所有编号是偶数的员工
- 名字是“张三”的员工
- 年龄超过25,薪资低于10000的男员工
示例代码:
Employee类:
public class Employee{ private int id; private String name; private char gender; private int age; private double salary; public Employee(int id, String name, char gender, int age, double salary) { super(); this.id = id; this.name = name; this.gender = gender; this.age = age; this.salary = salary; } public Employee() { super(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", salary=" + salary + "]"; } }
员工管理类:
class EmployeeService{ private ArrayList<Employee> all; public EmployeeService(){ all = new ArrayList<Employee>(); all.add(new Employee(1, "张三", '男', 33, 8000)); all.add(new Employee(2, "翠花", '女', 23, 18000)); all.add(new Employee(3, "无能", '男', 46, 8000)); all.add(new Employee(4, "李四", '女', 23, 9000)); all.add(new Employee(5, "老王", '男', 23, 15000)); all.add(new Employee(6, "大嘴", '男', 23, 11000)); } public ArrayList<Employee> get(Predicate<Employee> p){ ArrayList<Employee> result = new ArrayList<Employee>(); for (Employee emp : result) { if(p.test(emp)){ result.add(emp); } } return result; } }
测试类:
public class TestLambda { public static void main(String[] args) { EmployeeService es = new EmployeeService(); es.get(e -> true).forEach(e->System.out.println(e)); System.out.println(); es.get(e -> e.getAge()>35).forEach(e->System.out.println(e)); System.out.println(); es.get(e -> e.getSalary()>15000 && e.getGender()=='女').forEach(e->System.out.println(e)); System.out.println(); es.get(e -> e.getId()%2==0).forEach(e->System.out.println(e)); System.out.println(); es.get(e -> "张三".equals(e.getName())).forEach(e->System.out.println(e)); System.out.println(); es.get(e -> e.getAge()>25 && e.getSalary()<10000 && e.getGender()=='男').forEach(e->System.out.println(e)); } }
3. Java8新特性:方法引用与构造器引用
Lambda表达式是可以简化函数式接口的变量或形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。
3.1 方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法
对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。
3.1.1 方法引用格式
- 格式:使用方法引用操作符 “
::” 将类(或对象) 与 方法名分隔开来。
- 两个
:中间不能有空格,而且必须英文状态下半角输入
- 如下三种主要使用情况:
- 情况1:
对象 :: 实例方法名 - 情况2:
类 :: 静态方法名 - 情况3:
类 :: 实例方法名
3.1.2 方法引用使用前提
要求1: Lambda体只有一句语句,并且是通过调用一个对象的/类现有的方法来完成的
例如:System.out对象,调用println()方法来完成Lambda体
Math类,调用random()静态方法来完成Lambda体
要求2:
针对情况1:函数式接口中的抽象方法a在被重写时使用了某一个对象的方法b。如果方法a的形参列表、返回值类型与方法b的形参列表、返回值类型都相同,则我们可以使用方法b实现对方法a的重写、替换。
针对情况2:函数式接口中的抽象方法a在被重写时使用了某一个类的静态方法b。如果方法a的形参列表、返回值类型与方法b的形参列表、返回值类型都相同,则我们可以使用方法b实现对方法a的重写、替换。
针对情况3:函数式接口中的抽象方法a在被重写时使用了某一个对象的方法b。如果方法a的返回值类型与方法b的返回值类型相同,同时方法a的形参列表中有n个参数,方法b的形参列表有n-1个参数,且方法a的第1个参数作为方法b的调用者,且方法a的后n-1参数与方法b的n-1参数匹配(类型相同或满足多态场景也可以)
例如:t->System.out.println(t)
() -> Math.random() 都是无参
3.1.3 举例
public class MethodRefTest { // 情况一:对象 :: 实例方法 //Consumer中的void accept(T t) //PrintStream中的void println(T t) @Test public void test1() { Consumer<String> con1 = str -> System.out.println(str); con1.accept("北京"); System.out.println("*******************"); PrintStream ps = System.out; Consumer<String> con2 = ps::println; con2.accept("beijing"); } //Supplier中的T get() //Employee中的String getName() @Test public void test2() { Employee emp = new Employee(1001,"Tom",23,5600); Supplier<String> sup1 = () -> emp.getName(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<String> sup2 = emp::getName; System.out.println(sup2.get()); } // 情况二:类 :: 静态方法 //Comparator中的int compare(T t1,T t2) //Integer中的int compare(T t1,T t2) @Test public void test3() { Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2); System.out.println(com1.compare(12,21)); System.out.println("*******************"); Comparator<Integer> com2 = Integer::compare; System.out.println(com2.compare(12,3)); } //Function中的R apply(T t) //Math中的Long round(Double d) @Test public void test4() { Function<Double,Long> func = new Function<Double, Long>() { @Override public Long apply(Double d) { return Math.round(d); } }; System.out.println("*******************"); Function<Double,Long> func1 = d -> Math.round(d); System.out.println(func1.apply(12.3)); System.out.println("*******************"); Function<Double,Long> func2 = Math::round; System.out.println(func2.apply(12.6)); } // 情况三:类 :: 实例方法 (有难度) // Comparator中的int comapre(T t1,T t2) // String中的int t1.compareTo(t2) @Test public void test5() { Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2); System.out.println(com1.compare("abc","abd")); System.out.println("*******************"); Comparator<String> com2 = String :: compareTo; System.out.println(com2.compare("abd","abm")); } //BiPredicate中的boolean test(T t1, T t2); //String中的boolean t1.equals(t2) @Test public void test6() { BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2); System.out.println(pre1.test("abc","abc")); System.out.println("*******************"); BiPredicate<String,String> pre2 = String :: equals; System.out.println(pre2.test("abc","abd")); } // Function中的R apply(T t) // Employee中的String getName(); @Test public void test7() { Employee employee = new Employee(1001, "Jerry", 23, 6000); Function<Employee,String> func1 = e -> e.getName(); System.out.println(func1.apply(employee)); System.out.println("*******************"); Function<Employee,String> func2 = Employee::getName; System.out.println(func2.apply(employee)); } }
3.2 构造器引用
当Lambda表达式是创建一个对象,并且满足Lambda表达式形参,正好是给创建这个对象的构造器的实参列表,就可以使用构造器引用。
格式:类名::new
举例:
public class ConstructorRefTest { //构造器引用 //Supplier中的T get() //Employee的空参构造器:Employee() @Test public void test1(){ Supplier<Employee> sup = new Supplier<Employee>() { @Override public Employee get() { return new Employee(); } }; System.out.println("*******************"); Supplier<Employee> sup1 = () -> new Employee(); System.out.println(sup1.get()); System.out.println("*******************"); Supplier<Employee> sup2 = Employee :: new; System.out.println(sup2.get()); } //Function中的R apply(T t) @Test public void test2(){ Function<Integer,Employee> func1 = id -> new Employee(id); Employee employee = func1.apply(1001); System.out.println(employee); System.out.println("*******************"); Function<Integer,Employee> func2 = Employee :: new; Employee employee1 = func2.apply(1002); System.out.println(employee1); } //BiFunction中的R apply(T t,U u) @Test public void test3(){ BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name); System.out.println(func1.apply(1001,"Tom")); System.out.println("*******************"); BiFunction<Integer,String,Employee> func2 = Employee :: new; System.out.println(func2.apply(1002,"Tom")); } }
public class Employee { private int id; private String name; private int age; private double salary; public int getId() { return id; } public void setId(int id) { this.id = id; } 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 Employee() { System.out.println("Employee()....."); } public Employee(int id) { this.id = id; System.out.println("Employee(int id)....."); } public Employee(int id, String name) { this.id = id; this.name = name; } public Employee(int id, String name, int age, double salary) { this.id = id; this.name = name; this.age = age; this.salary = salary; } @Override public String toString() { return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}'; } }
3.3 数组构造引用
当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参,正好是给创建这个数组对象的长度,就可以数组构造引用。
格式:数组类型名::new
举例:
//数组引用 //Function中的R apply(T t) @Test public void test4(){ Function<Integer,String[]> func1 = length -> new String[length]; String[] arr1 = func1.apply(5); System.out.println(Arrays.toString(arr1)); System.out.println("*******************"); Function<Integer,String[]> func2 = String[] :: new; String[] arr2 = func2.apply(10); System.out.println(Arrays.toString(arr2)); }
4. Java8新特性:强大的Stream API
4.1 说明
- Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
- Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库
最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。 - Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
4.2 为什么要使用Stream API
实际开发中,项目中多数数据源都来自于MySQL、Oracle等。但现在数据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要Java层面去处理。
4.3 什么是Stream
Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,讲的是数据,而 Stream 是有关计算的,讲的是计算。 前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。
④ Stream一旦执行了终止操作,就不能再调用其它中间操作或终止操作了。
4.4 Stream的操作三个步骤
1- 创建 Stream
一个数据源(如:集合、数组),获取一个流
2- 中间操作
每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象。因此中间操作可以是个操作链,可对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。
3- 终止操作(终端操作)
终止操作的方法返回值类型就不再是Stream了,因此一旦执行终止操作,就结束整个Stream操作了。一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream。
4.4.1 创建Stream实例
方式一:通过集合
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
default Stream<E> stream(): 返回一个顺序流default Stream<E> parallelStream(): 返回一个并行流
@Test public void test01(){ List<Integer> list = Arrays.asList(1,2,3,4,5); //JDK1.8中,Collection系列集合增加了方法 Stream<Integer> stream = list.stream(); }
方式二:通过数组
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
- static <T> Stream<T>
stream(T[] array): 返回一个流 - public static IntStream
stream(int[] array) - public static LongStream
stream(long[] array) - public static DoubleStream
stream(double[] array)
@Test public void test02(){ String[] arr = {"hello","world"}; Stream<String> stream = Arrays.stream(arr); } @Test public void test03(){ int[] arr = {1,2,3,4,5}; IntStream stream = Arrays.stream(arr); }
方式三:通过Stream的of()
可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
public static<T> Stream<T> of(T... values): 返回一个流
@Test public void test04(){ Stream<Integer> stream = Stream.of(1,2,3,4,5); stream.forEach(System.out::println); }
方式四:创建无限流(了解)
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
- 迭代
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) - 生成
public static<T> Stream<T> generate(Supplier<T> s)
// 方式四:创建无限流 @Test public void test05() { // 迭代 // public static<T> Stream<T> iterate(final T seed, final // UnaryOperator<T> f) Stream<Integer> stream = Stream.iterate(0, x -> x + 2); stream.limit(10).forEach(System.out::println); // 生成 // public static<T> Stream<T> generate(Supplier<T> s) Stream<Double> stream1 = Stream.generate(Math::random); stream1.limit(10).forEach(System.out::println); }
4.4.2 一系列中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“ 惰性求值 ”。
1-筛选与切片
| 方 法 | 描 述 |
| filter(Predicatep) | 接收 Lambda , 从流中排除某些元素 |
| distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
| limit(long maxSize) | 截断流,使其元素不超过给定数量 |
| skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。 若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
2-映 射
| 方法 | 描述 |
| map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
| mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。 |
| mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。 |
| mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。 |
| flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
3-排序
| 方法 | 描述 |
| sorted() | 产生一个新流,其中按自然顺序排序 |
| sorted(Comparatorcom) | 产生一个新流,其中按比较器顺序排序 |
代码举例:
import org.junit.Test; import java.util.Arrays; import java.util.stream.Stream; public class StreamMiddleOperate { @Test public void test01(){ //1、创建Stream Stream<Integer> stream = Stream.of(1,2,3,4,5,6); //2、加工处理 //过滤:filter(Predicate p) //把里面的偶数拿出来 /* * filter(Predicate p) * Predicate是函数式接口,抽象方法:boolean test(T t) */ stream = stream.filter(t -> t%2==0); //3、终结操作:例如:遍历 stream.forEach(System.out::println); } @Test public void test02(){ Stream.of(1,2,3,4,5,6) .filter(t -> t%2==0) .forEach(System.out::println); } @Test public void test03(){ Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5) .distinct() .forEach(System.out::println); } @Test public void test04(){ Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5) .limit(3) .forEach(System.out::println); } @Test public void test05(){ Stream.of(1,2,2,3,3,4,4,5,2,3,4,5,6,7) .distinct() //(1,2,3,4,5,6,7) .filter(t -> t%2!=0) //(1,3,5,7) .limit(3) .forEach(System.out::println); } @Test public void test06(){ Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5) .skip(5) .forEach(System.out::println); } @Test public void test07(){ Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5) .skip(5) .distinct() .filter(t -> t%3==0) .forEach(System.out::println); } @Test public void test08(){ long count = Stream.of(1,2,3,4,5,6,2,2,3,3,4,4,5) .distinct() .peek(System.out::println) //Consumer接口的抽象方法 void accept(T t) .count(); System.out.println("count="+count); } @Test public void test09(){ //希望能够找出前三个最大值,前三名最大的,不重复 Stream.of(11,2,39,4,54,6,2,22,3,3,4,54,54) .distinct() .sorted((t1,t2) -> -Integer.compare(t1, t2))//Comparator接口 int compare(T t1, T t2) .limit(3) .forEach(System.out::println); } @Test public void test10(){ Stream.of(1,2,3,4,5) .map(t -> t+=1)//Function<T,R>接口抽象方法 R apply(T t) .forEach(System.out::println); } @Test public void test11(){ String[] arr = {"hello","world","java"}; Arrays.stream(arr) .map(t->t.toUpperCase()) .forEach(System.out::println); } @Test public void test12(){ String[] arr = {"hello","world","java"}; Arrays.stream(arr) .flatMap(t -> Stream.of(t.split("|")))//Function<T,R>接口抽象方法 R apply(T t) 现在的R是一个Stream .forEach(System.out::println); } }
4.4.3 终止操作
- 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
- 流进行了终止操作后,不能再次使用。
1-匹配与查找
| 方法 | 描述 |
| allMatch(Predicate p) | 检查是否匹配所有元素 |
| anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
| noneMatch(Predicatep) | 检查是否没有匹配所有元素 |
| findFirst() | 返回第一个元素 |
| findAny() | 返回当前流中的任意元素 |
| count() | 返回流中元素总数 |
| max(Comparator c) | 返回流中最大值 |
| min(Comparator c) | 返回流中最小值 |
| forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。 相反,Stream API 使用内部迭代——它帮你把迭代做了) |
2-归约
| 方法 | 描述 |
| reduce(T identity, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
| reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional<T> |
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
3-收集
| 方 法 | 描 述 |
| collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现, 用于给Stream中元素做汇总的方法 |
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
| 方法 | 返回类型 | 作用 |
| toList | Collector<T, ?, List<T>> | 把流中元素收集到List |
List<Employee> emps= list.stream().collect(Collectors.toList());
| 方法 | 返回类型 | 作用 |
| toSet | Collector<T, ?, Set<T>> | 把流中元素收集到Set |
Set<Employee> emps= list.stream().collect(Collectors.toSet());
| 方法 | 返回类型 | 作用 |
| toCollection | Collector<T, ?, C> | 把流中元素收集到创建的集合 |
Collection<Employee> emps =list.stream().collect(Collectors.toCollection(ArrayList::new));
| 方法 | 返回类型 | 作用 |
| counting | Collector<T, ?, Long> | 计算流中元素的个数 |
long count = list.stream().collect(Collectors.counting());
| 方法 | 返回类型 | 作用 |
| summingInt | Collector<T, ?, Integer> | 对流中元素的整数属性求和 |
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
| 方法 | 返回类型 | 作用 |
| averagingInt | Collector<T, ?, Double> | 计算流中元素Integer属性的平均值 |
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
| 方法 | 返回类型 | 作用 |
| summarizingInt | Collector<T, ?, IntSummaryStatistics> | 收集流中Integer属性的统计值。如:平均值 |
int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
| 方法 | 返回类型 | 作用 |
| joining | Collector<CharSequence, ?, String> | 连接流中每个字符串 |
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
| 方法 | 返回类型 | 作用 |
| maxBy | Collector<T, ?, Optional<T>> | 根据比较器选择最大值 |
Optional<Emp>max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
| 方法 | 返回类型 | 作用 |
| minBy | Collector<T, ?, Optional<T>> | 根据比较器选择最小值 |
Optional<Emp> min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
| 方法 | 返回类型 | 作用 |
| reducing | Collector<T, ?, Optional<T>> | 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值 |
int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
| 方法 | 返回类型 | 作用 |
| collectingAndThen | Collector<T,A,RR> | 包裹另一个收集器,对其结果转换函数 |
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
| 方法 | 返回类型 | 作用 |
| groupingBy | Collector<T, ?, Map<K, List<T>>> | 根据某属性值对流分组,属性为K,结果为V |
Map<Emp.Status, List<Emp>> map= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
| 方法 | 返回类型 | 作用 |
| partitioningBy | Collector<T, ?, Map<Boolean, List<T>>> | 根据true或false进行分区 |
Map<Boolean,List<Emp>> vd = list.stream().collect(Collectors.partitioningBy(Employee::getManage));
举例:
import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Test; public class StreamEndding { @Test public void test01(){ Stream.of(1,2,3,4,5) .forEach(System.out::println); } @Test public void test02(){ long count = Stream.of(1,2,3,4,5) .count(); System.out.println("count = " + count); } @Test public void test03(){ boolean result = Stream.of(1,3,5,7,9) .allMatch(t -> t%2!=0); System.out.println(result); } @Test public void test04(){ boolean result = Stream.of(1,3,5,7,9) .anyMatch(t -> t%2==0); System.out.println(result); } @Test public void test05(){ Optional<Integer> opt = Stream.of(1,3,5,7,9).findFirst(); System.out.println(opt); } @Test public void test06(){ Optional<Integer> opt = Stream.of(1,2,3,4,5,7,9) .filter(t -> t%3==0) .findFirst(); System.out.println(opt); } @Test public void test07(){ Optional<Integer> opt = Stream.of(1,2,4,5,7,8) .filter(t -> t%3==0) .findFirst(); System.out.println(opt); } @Test public void test08(){ Optional<Integer> max = Stream.of(1,2,4,5,7,8) .max((t1,t2) -> Integer.compare(t1, t2)); System.out.println(max); } @Test public void test09(){ Integer reduce = Stream.of(1,2,4,5,7,8) .reduce(0, (t1,t2) -> t1+t2);//BinaryOperator接口 T apply(T t1, T t2) System.out.println(reduce); } @Test public void test10(){ Optional<Integer> max = Stream.of(1,2,4,5,7,8) .reduce((t1,t2) -> t1>t2?t1:t2);//BinaryOperator接口 T apply(T t1, T t2) System.out.println(max); } @Test public void test11(){ List<Integer> list = Stream.of(1,2,4,5,7,8) .filter(t -> t%2==0) .collect(Collectors.toList()); System.out.println(list); } }
4.5 Java9新增API
新增1:Stream实例化方法
ofNullable()的使用:
Java 8 中 Stream 不能完全为null,否则会报空指针异常。而 Java 9 中的 ofNullable 方法允许我们创建一个单元素 Stream,可以包含一个非空元素,也可以创建一个空 Stream。
//报NullPointerException //Stream<Object> stream1 = Stream.of(null); //System.out.println(stream1.count()); //不报异常,允许通过 Stream<String> stringStream = Stream.of("AA", "BB", null); System.out.println(stringStream.count());//3 //不报异常,允许通过 List<String> list = new ArrayList<>(); list.add("AA"); list.add(null); System.out.println(list.stream().count());//2 //ofNullable():允许值为null Stream<Object> stream1 = Stream.ofNullable(null); System.out.println(stream1.count());//0 Stream<String> stream = Stream.ofNullable("hello world"); System.out.println(stream.count());//1
iterator()重载的使用:
//原来的控制终止方式: Stream.iterate(1,i -> i + 1).limit(10).forEach(System.out::println); //现在的终止方式: Stream.iterate(1,i -> i < 100,i -> i + 1).forEach(System.out::println);
4.6 示例
现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以
下若干操作步骤:
- 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
- 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
- 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
- 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
- 将两个队伍合并为一个队伍;存储到一个新集合中。
- 根据姓名创建 Person 对象;存储到一个新集合中。
- 打印整个队伍的Person对象信息。
Person 类的代码为:
public class Person { private String name; public Person() {} public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{name='" + name + "'}"; } }
两个队伍(集合)的代码如下:
public static void main(String[] args) { //第一支队伍 ArrayList<String> one = new ArrayList<>(); one.add("迪丽热巴"); one.add("宋远桥"); one.add("苏星河"); one.add("石破天"); one.add("石中玉"); one.add("老子"); one.add("庄子"); one.add("洪七公"); //第二支队伍 ArrayList<String> two = new ArrayList<>(); two.add("古力娜扎"); two.add("张无忌"); two.add("赵丽颖"); two.add("张三丰"); two.add("尼古拉斯赵四"); two.add("张天爱"); two.add("张二狗"); // ....编写代码完成题目要求 }
参考答案:
public static void main(String[] args) { //第一支队伍 ArrayList<String> one = new ArrayList<>(); one.add("迪丽热巴"); one.add("宋远桥"); one.add("苏星河"); one.add("石破天"); one.add("石中玉"); one.add("老子"); one.add("庄子"); one.add("洪七公"); //第二支队伍 ArrayList<String> two = new ArrayList<>(); two.add("古力娜扎"); two.add("张无忌"); two.add("赵丽颖"); two.add("张三丰"); two.add("尼古拉斯赵四"); two.add("张天爱"); two.add("张二狗"); // 第一个队伍只要名字为3个字的成员姓名; // 第一个队伍筛选之后只要前3个人; Stream<String> streamOne = one.stream().filter(s ‐> s.length() == 3).limit(3); // 第二个队伍只要姓张的成员姓名; // 第二个队伍筛选之后不要前2个人; Stream<String> streamTwo = two.stream().filter(s ‐> s.startsWith("张")).skip(2); // 将两个队伍合并为一个队伍; // 根据姓名创建Person对象; // 打印整个队伍的Person对象信息。 Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println); }
