该记录主要是java8的Lambda表达式以及Stream流的介绍与使用
一、Lambda表达式 1.使用介绍
举例: (o1,o2) -> Integer.compare(o1,o2);
格式:
-> :lambda操作符 或 箭头操作符
-> 左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
-> 右边:lambda体 (其实就是重写的抽象方法的方法体)
Lambda表达式的本质:作为函数式接口的实例
如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。我们可以在一个接口上使用 @FunctionalInterface 注解,
所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
总结:
->左边:lambda形参列表的参数类型可以省略(类型推断);如果lambda形参列表只有一个参数,其一对()也可以省略
->右边:lambda体应该使用一对{}包裹;如果lambda体只有一条执行语句(可能是return语句),省略这一对{}和return关键字
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 @Test public void test1 () { new Thread(() -> System.out.println("线程开开启了" )).start(); Runnable runnable = new Runnable() { @Override public void run () { System.out.println("匿名内部类 runnable……" ); } }; runnable.run(); Runnable r1 = () -> System.out.println("lambda runnable……" ); r1.run(); Consumer consumer = new Consumer() { @Override public void accept (Object o) { System.out.println("内部类实现 consumer……" ); } }; consumer.accept("111" ); Consumer consumer1 = (x) -> System.out.println("lambda 实现consumer" +x); consumer1.accept("lambda " ); }
2.java内置的4大核心函数式接口 (一)消费型接口 Consumer<T> void accept(T t)
有一个入参,无返回值
1 2 3 4 5 6 7 8 9 10 11 Consumer consumer = new Consumer() { @Override public void accept (Object o) { System.out.println("内部类实现 consumer……" ); } }; consumer.accept("111" ); Consumer consumer1 = (x) -> System.out.println("lambda 实现consumer" +x); consumer1.accept("lambda " );
(二) 供给型接口 Supplier<T> T get()
无入参 一个返回值
1 2 3 4 5 6 7 8 9 @Test public void test2(){ //供给型接口 Supplier<String> supplier = ()-> { String s2 ="3"; return s2.toUpperCase(); }; System.out.println("supplier.get() = " + supplier.get()); }
(三) 函数型接口 Function<T,R> R apply(T t)
一个入参,一个返回值
1 2 3 4 5 6 7 8 9 @Test public void test3 () { Function<String,String> function = (x)->{ System.out.println("x = " + x); return x.toUpperCase(Locale.ROOT); }; System.out.println("function = " + function.apply("liuzheng" )); }
(四) 断定型接口 Predicate<T> boolean test(T t)
一个入参,一个Boolean类型返回值
1 2 3 4 5 6 @Test public void test4 () { Predicate<String> predicate = (x) -> x.contains("liuzheng" ); System.out.println("predicate = " + predicate.test("fdfdliuzh3eng" )); }
3.方法引用 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
(一)方法引用的本质 方法引用本质上就是Lambda表达是,而Lambda表达式作为函数式接口的实例,所以方法引用也是函数式接口的实例
(二)使用方式 类(对象):: 方法名
(三)使用情况
对象 :: 非静态方法
它的形式参数全部传递给该方法作为参数
1 2 3 4 5 6 7 @Test public void test5 () { PrintStream out = System.out; Consumer<String> consumer = System.out::println; consumer.accept("方法引用" ); }
类 :: 静态方法
它的形式参数全部传递给该方法作为参数
1 2 Comparator<Integer> compare = Integer::compare; System.out.println("compare = " + compare.compare(1 , 4 ));
类 :: 非静态方法
第一个参数作为调用者 ,后面的参数全部传递给该方法作为参数
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 @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" )); } @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)); }
要求 针对方法1和方法2,要求接口中抽象方法的形参列表和返回值类型与方法引用的方法形参列表和返回值类型一致
4.构造器引用 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致
抽象方法的返回值类型即为构造器所属的类的类型
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 46 47 48 49 50 @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()); } @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); } @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" )); }
大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @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)); }
二、Stream流 常用操作
1 2 3 4 5 6 userList.stream().collect(Collectors.toMap(user::getUserId, t -> t, (oldvalue, newValue) -> newValue)); 参数说明: 1. user::getUserId 作为map的key 2. t->t value值为对象本身,也可以写 Function.identity() 3. (oldvalue, newValue) -> newValue) 当key值冲突时,key对应的value值覆盖为newValue
1.概要介绍
Stream关注的是对数据的运算,与CPU打交道
Stream 自己不会存储元素
Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
执行流程
Stream的实例化
一系列的中间操作(过滤、映射、…) 一个中间操作链,对数据源的数据进行处理
终止操作
一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
2.执行流程详解 (一)实例化 1. 通过集合 1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test1 () { List<Employee> employees = EmployeeData.getEmployees(); Stream<Employee> stream = employees.stream(); Stream<Employee> parallelStream = employees.parallelStream(); }
2. 通过数组 1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test2 () { int [] arr = new int []{1 ,2 ,3 ,4 ,5 ,6 }; IntStream stream = Arrays.stream(arr); Employee e1 = new Employee(1001 ,"Tom" ); Employee e2 = new Employee(1002 ,"Jerry" ); Employee[] arr1 = new Employee[]{e1,e2}; Stream<Employee> stream1 = Arrays.stream(arr1); }
3.通过Stream的of 1 2 3 4 5 @Test public void test3 () { Stream<Integer> stream = Stream.of(1 , 2 , 3 , 4 , 5 , 6 ); }
4.创建无限流 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void test4 () { Stream.iterate(0 , t -> t + 2 ).limit(10 ).forEach(System.out::println); Stream.generate(Math::random).limit(10 ).forEach(System.out::println); }
(二)中间链 1.筛选与切片 (一)filter filter(Predicate p)——接收 Lambda , 从流中排除某些元素。
1 2 List<Employee> employees = EmployeeData.getEmployees(); employees.stream().filter(e->e.getSalary()>100 ).forEach(System.out::println);
(二)limit limit(n)——截断流,使其元素不超过给定数量。
1 employees.stream().limit(3 ).forEach(System.out::println);
(三)skip skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
1 employees.stream().skip(3 ).forEach(System.out::println);
(四)distinct distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
1 2 3 4 5 6 7 8 9 list.add(new Employee(1010 ,"刘强东" ,40 ,8000 )); list.add(new Employee(1010 ,"刘强东" ,41 ,8000 )); list.add(new Employee(1010 ,"刘强东" ,40 ,8000 )); list.add(new Employee(1010 ,"刘强东" ,40 ,8000 )); list.add(new Employee(1010 ,"刘强东" ,40 ,8000 )); list.stream().distinct().forEach(System.out::println);
2.映射 (五)map map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
1 2 List<String> list = Arrays.asList("aa" , "bb" , "cc" , "dd" ); list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
(六)flatMap flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
1 2 3 4 5 6 7 8 9 10 11 12 13 Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringToStream); characterStream.forEach(System.out::println); public static Stream<Character> fromStringToStream (String str) { ArrayList<Character> list = new ArrayList<>(); for (Character c : str.toCharArray()){ list.add(c); } return list.stream(); }
(七)peek peek和map类似,peek方法接收一个Consumer的入参。了解λ表达式的应该明白 Consumer的实现类 应该只有一个方法,该方法返回类型为void。
正因为 peek()
不是一个最终操作,不会影响“哪些元素会流过”,所以十分适合在调试的时候,用来打印出流经管道的元素。例如:
1 2 3 4 5 6 Stream.of("one" , "two" , "three" , "four" ) .filter(e -> e.length() > 3 ) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList());
3.排序 (七)sorted() 自然排序 1 2 List<Integer> list = Arrays.asList(12 , 43 , 65 , 34 , 87 , 0 , -98 , 7 ); list.stream().sorted().forEach(System.out::println);
(八)sorted(Comparator com) 定制排序
1 2 3 4 5 6 7 8 9 10 11 12 List<Employee> employees = EmployeeData.getEmployees(); employees.stream().sorted( (e1,e2) -> { int ageValue = Integer.compare(e1.getAge(),e2.getAge()); if (ageValue != 0 ){ return ageValue; }else { return -Double.compare(e1.getSalary(),e2.getSalary()); } }).forEach(System.out::println); }
4.匹配与查找 (九) allMatch(Predicate p)
检查是否匹配所有元素。
1 2 3 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18 ); System.out.println(allMatch);
(十) anyMatch(Predicate p)
检查是否至少匹配一个元素。
1 2 3 boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000 ); System.out.println(anyMatch);
(十一) noneMatch(Predicate p)
检查是否没有匹配的元素。
1 2 3 boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷" )); System.out.println(noneMatch);
(十二) findFirst
返回第一个元素
1 2 Optional<Employee> employee = employees.stream().findFirst();
(十三) findAny
返回当前流中的任意元素
1 2 Optional<Employee> employee1 = employees.parallelStream().findAny(); System.out.println(employee1);
5. 收集 (十四)collect 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
1 2 3 4 5 6 7 8 List<Employee> employees = EmployeeData.getEmployees(); List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000 ).collect(Collectors.toList()); employeeList.forEach(System.out::println); System.out.println(); Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000 ).collect(Collectors.toSet()); employeeSet.forEach(System.out::println);
6.归约 (十五)reduce 可以将流中元素反复结合起来,得到一个值。返回 T
1 2 3 4 5 6 7 8 9 10 11 12 13 List<Integer> list = Arrays.asList(1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ); Integer sum = list.stream().reduce(0 , Integer::sum); System.out.println(sum); List<Employee> employees = EmployeeData.getEmployees(); Stream<Double> salaryStream = employees.stream().map(Employee::getSalary); Optional<Double> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2); System.out.println(sumMoney.get());
7.其它 (十六)count 1 2 3 4 List<Employee> employees = EmployeeData.getEmployees(); long count = employees.stream().filter(e -> e.getSalary() > 5000 ).count();System.out.println(count);
(十七)max 1 2 3 4 Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary()); Optional<Double> maxSalary = salaryStream.max(Double::compare); System.out.println(maxSalary);
(十八)min
1 2 3 Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); System.out.println(employee);
(十九)forEach
1 2 employees.stream().forEach(System.out::println);
三、Optional类 1、ofNullable
不管所传入的值为null不为null,创建的时候都不会报错
Optional.ofNullable(..).orElse(..) 不管所传入的值为null不为null,创建的时候都不会报错底层是一个包装好的三元运算,避免空指针
示例
结果 = Optional.ofNullable(值1).orElse(值2)
值1不空取值1,值1空了取值2
避免踩坑
字符串不要使用,”” 判定为非空
Optional.ofNullable(m1()).orElse(m2()) 即使 m1 非空,也会调用 m2 ,因此当m2方法有大量计算,或数据操作时,避免使用,可以使用 orElseGet()
2、of 1 2 3 4 Optional ofOptional14 = Optional.of("张三" );
3、empty 1 2 3 4 5 6 7 8 9 Optional<String> emptyOptional7 = Optional.empty(); Optional stringOptional = Optional.of("张三" ); System.out.println(stringOptional.get()); Optional emptyOptional8 = Optional.empty();
4、orElse 如果Optional对象不为空,则返回值,如果为空,则返回指定值,如果此处使用的是函数,则无论 Optional 对象为不为空都将出发方法的调用
1 2 3 4 5 6 Optional stringOptional9 = Optional.of("张三" ); System.out.println(stringOptional9.orElse("liuzhegn" )); Optional emptyOptional11 = Optional.empty(); System.out.println(emptyOptional11.orElse("李四" ));
5、orElseGet 1 2 3 4 Optional stringOptional12 = Optional.of("张三" ); System.out.println(stringOptional12.orElseGet(() -> "zhangsan" )); Optional emptyOptional13 = Optional.empty(); System.out.println(emptyOptional13.orElseGet(() -> "orElseGet" ));
6、filter 如果创建的Optional中的值满足filter中的条件,则返回包含该值的Optional对象,否则返回一个空的Optional对象
注意Optional中的filter方法和Stream中的filter方法是有点不一样的,Stream中的filter方法是对一堆元素进行过滤,而Optional中的filter方法只是对一个元素进行过滤,可以把Optional看成是最多只包含一个元素的Stream。
1 2 3 4 Optional stringOptional16 = Optional.of("zhangsan" ); System.out.println(stringOptional16.filter(e -> e.equals("zhangsan" )).orElse("王五" )); stringOptional = Optional.empty(); System.out.println(stringOptional.filter(e -> e.equals("zhangsan" )).orElse("lisi" ));
7、ifPresent ifPresent方法的参数是一个Consumer的实现类,Consumer类包含一个抽象方法,该抽象方法对传入的值进行处理,只处理没有返回值。
1 2 Optional stringOptional2 = Optional.of("zhangsan" ); stringOptional2.ifPresent(e-> System.out.println("我被处理了。。。" +e));
8、isPresent isPresent 如果有值返回true,否则返回false
1 2 3 Optional stringOptional3 = Optional.of("zhangsan" ); boolean present = stringOptional3.isPresent();System.out.println("present = " + present);
9、map map方法执行传入的lambda表达式参数对Optional实例的值进行修改,修改后的返回值仍然是一个Optional对象
1 2 3 4 Optional<String> stringOptional4 = Optional.of("zhangsan" ); System.out.println(stringOptional4.map(e -> e.toUpperCase()).orElse("失败" )); stringOptional4 = Optional.empty(); System.out.println(stringOptional4.map(e -> e.toUpperCase()).orElse("失败" ));