【Java基础】JDK8新特性最佳实践2

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 【Java基础】JDK8新特性最佳实践2

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);
}

800dbb1ec1bc4f288f77a3bb12d9ea66.jpeg

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);   
}

69d43bf3de7849b0908ef65f9d067eb1.jpeg

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);
    }

9eb5a2d4767f4b448d78538015bd82f1.jpeg

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();
    }

0fde1be85f234991ac9efdd312d21362.jpeg

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;
    }

32878c019df74991ae4971a408757aa7.jpeg

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;
    }

ed6f65a38f4f44e28bfaf1028d36b6e7.jpeg

7.Stream流式操作集合框架

7.1.Stream简介

(1)什么是stream

Stream中文称为"流",通过将集合转化为这么一种叫做流的元素队列,通过声明性方式,能够对集合中的每一个元素进行一些列并行或穿行的流水线操作。
元素是特定类型的对象,所以元素集合看作一种流,流在管道中传输,且可以在管道的节点上进行处理,比如 排序,聚合,过滤等

96c12e6a2d4443fcb0e09b9863a5041b.jpeg

  • ·操作详情
  • 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);
        }

e9ec04c62d4b4b70ac38871c2955f6db.jpeg

7.2.map和filter函数

(1)map函数

  • ·将流中的每一个元素T映射成R(类似类型转换)
  • ·应用场景:转换对象,类似DO对象转换成DTO对象
  • ·map函数源码

dc7608469df1404cb4fd7080c236683e.jpeg

  • ·测试
    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);
        }
    }

0b1c92f76a18417a807f323d575cf237.jpeg

(2)filter函数

  • ·用于通过设置的条件过滤出元素
  • ·应用场景:过滤出符合条件的元素
  • ·filter函数源码

a95430801c6c41fba4ef852d21d750c0.jpeg

  • ·测试
 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);

f4c88e940fbb495184720ec282d50c8f.jpeg

7.3.sorted和limit函数

(1)sorted函数

  • ·sorted()对流进行自然的排序,其中的元素必须实现Comparable接口
  • ·应用场景:需要对集合的元素进行定制化排序
  • ·sorted源码

4261204b959a481795b30b86c5185925.jpeg

  • ·测试
//排序,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);

d44e9e6758634745aef3b65c8e7855c6.jpeg

(2)limit函数

  • ·limit()截断流使其最多包含指定的数量的元素
  • ·应用场景:排行榜,截取前多少名
  • ·limit源码

ba27e538ecdb428d81300e3d667207b2.jpeg

  • ·测试
//根据长度排序,降序,截取前三个
List<String> collect4 = list.stream()
       .sorted(Comparator.comparing(String::length,Comparator.reverseOrder())).limit(3)
       .collect(Collectors.toList());
System.out.println(collect4);

0a93b68672fe47cdbf318c164f525b44.jpeg

1f6f25b5d9b6480b9940e0dab1d44f18.jpeg

7.4.allMatch和anyMatch函数

(1)allMatch函数

  • ·检查是否匹配所有元素,主有全部符合才返回true
  • ·allMatch源码

13675347819a4c148213e4b2b008c340.jpeg

  • ·测试
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);

a571bf20d6f947108a6ec6f75a3930cd.jpeg

(2)anyMatch函数

  • ·检查是否匹配所有元素,主有全部符合才返回true
  • ·anyMatch源码

385763edfabf4ed6a76b1c7be6136008.jpeg

  • ·测试
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);

17afbc3354e94758ac97a8dc323f03b6.jpeg

7.4.max和min函数

(1)max函数

  • ·获取集合元素中的最大值
  • ·max源码

1df6dab753364e21b9efd6608dde5aca.jpeg

  • ·测试
Optional<Student> max = list.stream().max(Comparator.comparingInt(Student::getAge));
if (max.isPresent()){
    System.out.println("最大年龄:"+max.get().getAge());
}

7da0bf35f48d42de988afd2b36330a4e.jpeg

(2)min函数

  • ·获取集合中的最小值
  • ·min源码

6eb5557904bc476894b953d009196eb1.jpeg

  • ·测试
Optional<Student> min = list.stream().min((student1, student2) -> {
     return Integer.compare(student1.getAge(), student2.getAge());
});
if(min.isPresent()){
     System.out.println("最小年龄:"+min.get().getAge());
}

ef26f25357ed4a7ea205f956f517d21c.jpeg

7.5.并行流parallelStream

  • ·为什么会有这个并行流
  • o 集合做重复的操作,如果使用串行执行会相当耗时,因此一般会采用多线程来加快,Java8的paralleStream用fork/join框架提供了并发执行能力
  • o底层原理
  • ·线程池(ForkJoinPool)维护一个线程队列
  • ·可以分割任务,将父任务拆分成子任务,完全贴合分治思想
  • ·fork/join框架

17b0a5d90129486a8c701cca75cac74c.jpeg

  • ·两个区别
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+" "));

0a589df65c264b8c81c8a1c99987eedc.jpeg

  • ·存在问题
  • ·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());
}

52afd69003734277a1278a30fd549a31.jpeg

7.6.reduce函数

  • ·什么是reduce操作
  • o聚合操作,中文意思是“减少”
  • o根据一定的规则将Stream中的元素进行计算后返回一个唯一的值
  • o源码分析

124612b8690e4bbc94d376c2b42da0be.jpeg

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);

fdd11e0f9be04197904b028ae525156c.jpeg

7.7.集合foreach

  • ·集合遍历方式
  • ofor循环
  • o迭代器Iterator
  • ·源码分析

72cdeb0dfe6f4c8396e2bc177319294c.jpeg

  • ·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()));

a718385f012749f19dc11908f51656c4.jpeg

  • ·注意点:
  • o不能修改包含外部的变量的值
  • o不能用break或者return或者continue等关键词结束或者跳出循环



相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
16天前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
26天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
22天前
|
容器
jdk8新特性-详情查看文档
jdk8新特性-详情查看文档
39 7
|
20天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
20天前
|
Java
Java 异常处理:11 个异常处理最佳实践
本文深入探讨了Java异常处理的最佳实践,包括早抛出晚捕获、只捕获可处理异常、不忽略异常、抛出具体异常、正确包装异常、记录或抛出异常但不同时执行、不在finally中抛出异常、避免用异常控制流程、使用模板方法减少重复代码、抛出与方法相关的异常及异常处理后清理资源等内容,旨在提升代码质量和可维护性。
|
25天前
|
存储 Java 数据挖掘
Java 8 新特性之 Stream API:函数式编程风格的数据处理范式
Java 8 引入的 Stream API 提供了一种新的数据处理方式,支持函数式编程风格,能够高效、简洁地处理集合数据,实现过滤、映射、聚合等操作。
41 6
|
25天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
26天前
|
运维 Java 编译器
Java 异常处理:机制、策略与最佳实践
Java异常处理是确保程序稳定运行的关键。本文介绍Java异常处理的机制,包括异常类层次结构、try-catch-finally语句的使用,并探讨常见策略及最佳实践,帮助开发者有效管理错误和异常情况。
80 4
|
27天前
|
Oracle 安全 Java
深入理解Java生态:JDK与JVM的区分与协作
Java作为一种广泛使用的编程语言,其生态中有两个核心组件:JDK(Java Development Kit)和JVM(Java Virtual Machine)。本文将深入探讨这两个组件的区别、联系以及它们在Java开发和运行中的作用。
69 1