6.函数式编程
6.1.Java8内置的四大函数式接口
·Lambda表达式必须先定义接口,创建相关方法之后可使用,这样做十分不方便,java8已经内置了许多接口,例如下面四个功能性接口,所以一般很少会由用户去定义新的函数时接口。
· java8的最大特性就是函数式接口,所有标注了@FunctionalInterface注解的接口都是函数式接口。
Consumer<T>:消费型接口,有入参,无返回值。 void accept(T t); Supplier<T>:供给型接口,无入参,有返回值。 T get(); Function<T,R>:函数型接口,有入参,无返回值 R apply(T t); Predicate<T>:断言型接口,有入参,有返回值,返回值类型确定是boolean boolean test(T t);
6.2.函数式编程Function
- ·Function
- o 传入一个值经过函数的计算返回另一个值
- o T:入参类型,R:出参类型
@FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * @param t the function argument * @return the function result */ R apply(T t); }
- ·作用:将转换后逻辑提取出来,解耦合
public class FunctionObj implements Function { @Override public Object apply(Object o) { return o+":apply处理"; } }
public static void main(String[] args) { Object lixiang = apply("lixiang", new FunctionObj()); System.out.println(lixiang); } public static Object apply(String o,FunctionObj obj){ return obj.apply(o); }
6.3.函数式编程BiFunction
- ·BiFunction Function只能接受一个参数,如果要传递两个参数,则用BiFunction。
@FunctionalInterface public interface BiFunction<T,U,R>{ R apply(T t,U u); }
public static void main(String[] args) { System.out.println(operator(10,21,(a,b)->a+b)); System.out.println(operator(10,2,(a,b)->a-b)); System.out.println(operator(8,4,(a,b)->a*b)); System.out.println(operator(10,2,(a,b)->a/b)); } public static Integer operator(Integer a, Integer b, BiFunction<Integer,Integer, Integer> bf) { return bf.apply(a, b); }
6.4.函数式编程Consumer
- ·Consumer消费型接口:有入参,无返回值
- ·将 T 作为输入,不反回任何内容
- o调用方法:void accept(T t);
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
- ·用途:因为没有出参常用于打印,发送短信等消费动作
public static void main(String[] args) { sendMessage("11111111",phone -> System.out.println(phone+"已经被发送")); } public static void sendMessage(String phone, Consumer<String> consumer){ consumer.accept(phone); }
6.5.函数式编程Supplier
- ·Supplier:供给型接口:无入参,有返回值
- ·T:出参类型
- o调用方法:T get();
@FunctionalInterface public interface Supplier<T> { T get(); }
- ·用途:泛型一定和方法的返回值是一种类型,如果需要获得一个数据,并且不需要传入参数,可以使用Supplier接口,例如无参的工厂方法。
public static void main(String[] args) { Student student = newStudent(); System.out.println(student); } public static Student newStudent(){ Supplier<Student> supplier=()-> new Student("lixiang",20); return supplier.get(); }
6.6.函数式编程Predicate
- ·Predicate:断言型接口:有入参,有出参,返回值类型是boolean
- ·T:入参类型,出餐类型是Boolean
- ·调用方法:boolean test(T t)
@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
- ·用途:接收一个参数,用于判断是否满足一定的条件过滤数据
public static void main(String[] args) { List<String> list = Arrays.asList("awewrwe","vdssdsd","aoooo","psdddsd"); List<String> results = filter(list, obj -> obj.startsWith("a")); System.out.println(results); } public static List<String> filter(List<String> list,Predicate<String> predicate){ List<String> results = new ArrayList<>(); for (String s : list) { if(predicate.test(s)){ results.add(s); } } return results; }
6.7.方法与构造方法引用
- ·以前调用方法:对象.方法名(),类名.方法名()
- ·JDK8提供了另外一种方式 :: (双冒号)
方法引用是一种更简洁易懂的lambda表达式,操作符是双冒号::,用来直接访问类或者实例已经存在的方法或者构造方法。 通过语法引用,可以将方法的引用赋值给一个变量 语法:左边是容器(可以是类名,实例名),中间是"::",右边是相应的方法名 静态方法:ClassName::methodName 实例方法:Instance::methodName 构造函数:类名::new 单个参数 Function<入参1,返回类型> func = 方法引用 应用 func.apply(݇入参); 两个参数 BiFunction<݇入参1, 入参2> func = 方法引用 应用 func.apply(入参1,入参2);
测试调用
public static void main(String[] args) { //静态方法的调用 Function<String,Integer> function1 = Integer::parseInt; Integer num = function1.apply("1024"); System.out.println("转化后:"+num); //实例方法的调用 String context = "lixiang"; Function<Integer,String> function2 = context::substring; String str = function2.apply(1); System.out.println("截取后的字符串:"+str); //构造方法的调用,双参数 BiFunction<String,Integer,Student> function3 = Student::new; Student lixiang = function3.apply("lixiang", 20); System.out.println(lixiang); //构造方法的调用,单个参数 Function<String,Student> function4 = Student::new; Student lisi = function4.apply("lisi"); System.out.println(lisi); //函数作为参数传递到另一个方法中 String sayHello = sayHello(String::toUpperCase, "lixiang"); System.out.println(sayHello); } public static String sayHello(Function<String,String> function,String str){ String apply = function.apply(str); return apply; }
7.Stream流式操作集合框架
7.1.Stream简介
(1)什么是stream
Stream中文称为"流",通过将集合转化为这么一种叫做流的元素队列,通过声明性方式,能够对集合中的每一个元素进行一些列并行或穿行的流水线操作。 元素是特定类型的对象,所以元素集合看作一种流,流在管道中传输,且可以在管道的节点上进行处理,比如 排序,聚合,过滤等
- ·操作详情
- o数据元素就是原始的集合,List、Map、Set等
- o生成流,可以是串行流stream()或者并行流parallelStream()
- o中间操作,可以是排序,聚合,过滤,转换等
- o终端操作,很多流操作本身就会返回一个流,所以多个操作可以直接连接起来,最后统一收集
(2)案例
List<String> list = Arrays.asList("SpringBoot", "SpringCloud", "Redis", "RabbitMQ"); List<String> collect = list.stream().map(obj -> obj + "-拼接").collect(Collectors.toList()); for (String s : collect) { System.out.println(s); }
7.2.map和filter函数
(1)map函数
- ·将流中的每一个元素T映射成R(类似类型转换)
- ·应用场景:转换对象,类似DO对象转换成DTO对象
- ·map函数源码
- ·测试
public static void main(String[] args) { List<User> users = Arrays.asList( new User(1, "小东", "123"), new User(2, "小李", "123"), new User(3, "小王", "123"), new User(4, "小张", "123")); List<UserDTO> collect = users.stream().map(user -> { UserDTO userDTO = new UserDTO(); userDTO.setUserId(user.getId()); userDTO.setUsername(user.getName()); return userDTO; }).collect(Collectors.toList()); for (UserDTO userDTO : collect) { System.out.println(userDTO); } }
(2)filter函数
- ·用于通过设置的条件过滤出元素
- ·应用场景:过滤出符合条件的元素
- ·filter函数源码
- ·测试
List<String> list = Arrays.asList("SpringBoot", "SpringCloud", "Redis", "RabbitMQ","JUC"); //filter List<String> collect5 = list.stream().filter(str -> str.length() > 4).collect(Collectors.toList()); System.out.println(collect5);
7.3.sorted和limit函数
(1)sorted函数
- ·sorted()对流进行自然的排序,其中的元素必须实现Comparable接口
- ·应用场景:需要对集合的元素进行定制化排序
- ·sorted源码
- ·测试
//排序,sort(),默认升序 List<String> collect1 = list.stream().sorted().collect(Collectors.toList()); System.out.println("sort()按照英文字母升序:"+collect1); //根据长度排序,默认升序 List<String> collect2 = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList()); System.out.println("sort()按照英文字母的长度升序:"+collect2); //根据长度排序,降序 List<String> collect3 = list.stream().sorted(Comparator.comparing(obj -> obj.length(),Comparator.reverseOrder())).collect(Collectors.toList()); System.out.println("sort()按照英文字母的长度降序:"+collect3);
(2)limit函数
- ·limit()截断流使其最多包含指定的数量的元素
- ·应用场景:排行榜,截取前多少名
- ·limit源码
- ·测试
//根据长度排序,降序,截取前三个 List<String> collect4 = list.stream() .sorted(Comparator.comparing(String::length,Comparator.reverseOrder())).limit(3) .collect(Collectors.toList()); System.out.println(collect4);
7.4.allMatch和anyMatch函数
(1)allMatch函数
- ·检查是否匹配所有元素,主有全部符合才返回true
- ·allMatch源码
- ·测试
List<String> list = Arrays.asList("SpringBoot", "SpringCloud", "Redis", "RabbitMQ","Netty","JUC","Docker"); boolean allFlag = list.stream().allMatch(str -> str.length()>5); System.out.println("allFlag全部满足返回true:"+allFlag);
(2)anyMatch函数
- ·检查是否匹配所有元素,主有全部符合才返回true
- ·anyMatch源码
- ·测试
List<String> list = Arrays.asList("SpringBoot", "SpringCloud", "Redis", "RabbitMQ","Netty","JUC","Docker"); boolean allFlag = list.stream().anyMatch(str -> str.length()>5); System.out.println("allFlag全部满足返回true:"+allFlag);
7.4.max和min函数
(1)max函数
- ·获取集合元素中的最大值
- ·max源码
- ·测试
Optional<Student> max = list.stream().max(Comparator.comparingInt(Student::getAge)); if (max.isPresent()){ System.out.println("最大年龄:"+max.get().getAge()); }
(2)min函数
- ·获取集合中的最小值
- ·min源码
- ·测试
Optional<Student> min = list.stream().min((student1, student2) -> { return Integer.compare(student1.getAge(), student2.getAge()); }); if(min.isPresent()){ System.out.println("最小年龄:"+min.get().getAge()); }
7.5.并行流parallelStream
- ·为什么会有这个并行流
- o 集合做重复的操作,如果使用串行执行会相当耗时,因此一般会采用多线程来加快,Java8的paralleStream用fork/join框架提供了并发执行能力
- o底层原理
- ·线程池(ForkJoinPool)维护一个线程队列
- ·可以分割任务,将父任务拆分成子任务,完全贴合分治思想
- ·fork/join框架
- ·两个区别
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10); //串行流 System.out.print("串行流:"); numbers.stream().forEach(num -> System.out.print(num+" ")); System.out.println(); //并行流 System.out.print("并行流:"); numbers.parallelStream().forEach(num -> System.out.print(num+" "));
- ·存在问题
- ·parallelStream并行是否一定比Sream串行快?
- o 错误,数据量少的情况,可能串行更快,ForkJoin会耗性能
- ·多数情况下并行比串行块,是否可以都用并行
- o 不行,部分情况会线程安全问题,parallelStream里面使用的外部变量,比如集合一定要使用线程 安全集合,不然就会引用多线程安全问题
for (int i = 0; i < 10; i++) { //List<Integer> list = new ArrayList<>(); CopyOnWriteArrayList list = new CopyOnWriteArrayList(); IntStream.range(1,100).parallel().forEach(list::add); System.out.println(list.size()); }
7.6.reduce函数
- ·什么是reduce操作
- o聚合操作,中文意思是“减少”
- o根据一定的规则将Stream中的元素进行计算后返回一个唯一的值
- o源码分析
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7); //将集合内的元素相加 Integer integer = list.stream().reduce((x, y) -> x + y).get(); System.out.println("集合内元素相加:"+integer); //初始值100,将集合内的元素相加 Integer integer1 = list.stream().reduce(100,(x,y) -> x+y); System.out.println("100累加集合内的元素:"+integer1); //判断最大值返回 Integer integer2 = list.stream().reduce((x, y) -> x > y ? x : y).get(); System.out.println("集合内最大元素:"+integer2);
7.7.集合foreach
- ·集合遍历方式
- ofor循环
- o迭代器Iterator
- ·源码分析
- ·JDK8里面的新增接口
List<Student> result = Arrays.asList(new Student(32),new Student(33),new Student(21),new Student(29),new Student(18)); result.forEach(obj -> System.out.println(obj.getAge()));
- ·注意点:
- o不能修改包含外部的变量的值
- o不能用break或者return或者continue等关键词结束或者跳出循环