Java核心技术之stream详解+Java8及以后的新特性

简介: 如何更好的使用Lambda表达式,优雅的使用Stream操作数据的切片、筛选、过滤,以及在大数据量下归类与计算利器Map/Reduce

image.png

前言:📫 作者简介:小明java问道之路,专注于研究计算机底层,就职于金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的设计和架构📫

🏆 Java领域优质创作者、阿里云专家博主、华为云享专家🏆

🔥 如果此文还不错的话,还请👍关注点赞收藏三连支持👍一下博主哦

本文导读

如何更好的使用Lambda表达式,优雅的使用Stream操作数据的切片、筛选、过滤,以及在大数据量下归类与计算利器Map/Reduce

第一节:Java8新特性有哪些?

官网文档:What's New in JDK 8

重要的:

1、在接口中新增了default方法和static方法,这两种方法可以有方法体 (*default方法可以被子接口继承亦可被其实现类所调用,default方法被继承时,可以被子接口覆写;接口中的static方法不能被继承,也不能被实现类调用,只能被自身调用)

2、Lambda表达式(可以看成是匿名内部类,->表示连接符;{}内部是方法体;<实例>::<实例方法名>返回函数式接口,例如Collections::sort)

3、函数式接口(如果一个接口只有一个抽象方法)可以使用Lambda表达式,lambda表达式会被匹配到这个抽象方法上

4、Lambda作用域(可以直接访问标记了final的外层局部变量,实例的字段以及静态变量,lambda内部对于实例的字段以及静态变量是即可读又可写)

5、访问接口的方法:Predicate接口(有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法);Comparator 在Java 8在此之上添加了多种默认方法

6、Optional 接口(防止NullPointerException异常的辅助类型)

7、Date api(Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数;LocalTime 本地时间类LocalDate 本地日期类 )

8、Annotation 注解,8中支持多重注解了



第二节:为什么要用Lambda表达式?

case1:当一个类,仅对另一个类有用,使用匿名内部类或嵌套类就可以解决实际问题时,为了简化程序同时可以提高封装性,避免误用

8以前:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() { // TODO
    }
});


8+:

Thread thread = new Thread(() -> {});


case2:对集合完成遍历、过滤、搜索等计算

8以前:for while if else循环等

8+

List result = list.stream().filter(() -> // TODO
).collect(Collectors.toList());


总结:lambdajava提供了一个代码优雅的方式


第三节:Stream的常用操作

Java8对集合提供了一种流式计算的方式, 流在管道中传输, 并且可以在管道的节点上进行处理,比如筛选,排序,聚合等。

Stream API 基本都是返回Stream本身(这样多个操作可以串联成一个管道)

1、Stream创建有两种:stream() 串行流、paralleStream() 并行流(大量数据推荐使用paralleStream())

2、stream内部遍历的方式forEach(),通过forEach可以大大简化集合遍历的代码

3、stream用于数据过滤的方法filter(),实现数据过滤

4、stream用于数据映射的方法map(),实现数据映射(获取数据)

5、stream用于数据映射的方法sorted(),实现数据排序

6、Collectors 类实现了很多归约(保持数据原貌的前提下,最大限度地精简数据量)操作,例如将流转换成集合和聚合元素。Collectors()可用于返回列表或字符串

7、统计结果的收集器也非常有用,它们主要用于int、double、long等基本类型上,如mapToInt()

8、Steam提供的flatMap()的作用是对流的元素进行一对多转换,然后将结果元素放到一个新的流

9、在大数量的场景下可以可以采用分而治之的思想来进行计算,stream里提供了reduce()相关的操作

case1:遍历集合取出不为空的集合并排序:

list.stream().filter(x -> x != null)
    .sorted(Comparator.comparing(Object::hashCode)).collect(Collectors.toList());

image.gif

case2:求学生总成绩:

studentList.parallelStream()
    .map(Student::getScore).reduce((s1, s2) -> s1 + s2).get();


case3:收集器:

IntSummaryStatistics stats = numberList.stream()
                .mapToInt((x) -> x).summaryStatistics();


case4:流聚合:

// listOfLists = Arrays.asList(list);
listOfLists.stream().flatMap(x -> x.stream).collect(Collectors.toList());
// 收集器使用
System.out.println(stats.getMax() + stats.getMin() + stats.getSum() + stats.getAverage());


第四节:Stream几乎全部操作

1、筛选:

filter接受一个条件(一个返回boolean的函数)作为参数,并返回一个包括所有符合 条件 的元素的流

distinct:流还支持一个叫作distinct的方法,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流

2、切片:

takeWhile采用filter的缺点是,你需要遍历整个流中的数据,对其中的每一个元素执行 条件 操作。takeWhile操作就是为此而生的,它可以帮助你利用 条件 对流进行分片(即便你要处理的流是无限流也毫无困难)但是会在遭遇第一个不符合要求的元素时停止处理。

dropWhile:dropWhile操作是对takeWhile操作的补充。它会从头开始,丢弃所有 条件 结果为false的元素。一旦遭遇 条件 计算的结果为true,它就停止处理,并返回所有剩余的元素,即便要处理的对象是一个由无限数量元素构成的流,它也能工作得很好

3、截断:limit返回另一个不超过给定长度的流。所需的长度作为参数传递给limit,如果流是有序的,则最多会返回前n个元素

4、跳过:skip返回一个扔掉了前n个元素的流

5、映射:

map:流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”)

flatMap:使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。

6、匹配:                                                                

allMatchanyMatchnoneMatchfindFirstfindAny

anyMatch:可以匹配“流中是否有一个元素能匹配给定的 条件 ”,allMatch方法的工作原理和anyMatch类似,但它会看看流中的元素是否都能匹配给定的 条件

7、短路:

anyMatchallMatchnoneMatch这三个操作都用到了所谓的短路,这就是大家熟悉的Java中&&和||(不管表达式有多长,你只需找到一个表达式为false,就可以推断整个表达式将返回false,所以用不着计算整个表达式)

8、查找:

findAny查找元素findAny方法将返回当前流中的任意元素

findFirst:查找第一个元素

何时使用findFirst和findAny?并行。找到第一个元素在并行上限制更多。如果你不关心返回的元素是哪个,使用findAny,因为它在使用并行流时限制较少

9、归约:reduce方法将流归约成一个值。用函数式编程语言的术语来说,这称为折叠(fold),通俗来说就是保持数据原貌的前提下,减少数据量

10、数值流:使用reduce方法计算流中元素的总和

第五节:Java91011的新增主要特性

java9

1没有jre的目录

2模块化(可以创建类型为 package-info.java的类,这个东西可以减少jvm的负担,想启动的时候,jvm只会启动和它有关的模块,不会加载所有模块提高性能)

3可以兼容多版本(向下兼容)

4interface 支持私有方法(private *** java9(){})

5钻石操作符(原:Handler<Integer> intHandler = new Handler<Integer>(1) { }; 现:Handler<Integer> intHandler = new Handler<>(1) {};)

6String底层存储结构更换(java8之前Strin的底层结构类型都是char[],但是java9就替换成 byte[] ,java9会自动识别用哪个编码,当数据用1byte,就会使iSO或者latin1,当空间数据满2byte的时候自动使utf-1节省空间,提高性能,同理,StringBuilder StringBuffer也更换了底层数据结构)

7Stream API 新添加了4个方法,takeWhile、dropWhile、ofNullable、iterate(新重载方法)(takeWhile当达到一定条件就结束;ofNullable在java9创建nulliterate 不加条件无线循环)

8引进HttpClient(不用通过maven添加httpclient,直接可以用)

9Collection API的增强功能(增的工厂方法可以简化小规模List、Set或者Map的创建List.of、Set.of、Map.of)

10默认使用G1GC

参考资料:Java Platform, Standard Edition What’s New in Oracle JDK 9, Release 9

Java10(优化为主)

1局部变量的类型推断 var关键字(var list = new ArrayList<String>();)

2合并 JDK 多个代码仓库到一个单独的储存库中(在 JDK9 中,有8个仓库:root、corba、hotspot、jaxp、jaxws、jdk、langtools 和 nashorn这些将被合并为一个)

3GC改进和内存管理并行全垃圾回收器 G1(通过完全GC并行来改善G1最坏情况的等待时间)

4线程-局部变量管控(将允许在不运行全局虚拟机安全点的情况下实现线程回调)

5垃圾回收器接口

6List、Map、Set都增加了一个新的静态方法,copyOf(Collection)(这些函数按照其迭代顺序返回一个不可修改的列表、映射或包含给定集合的元素的集合。)

7ByteArrayOutputStream(字节数组输出流,通过使用指定的字符集解码字节,将缓冲区的内容转换为字符串)

参考资料:JDK 10 Release Notes

Java11*

1本地变量类型推断(通过var推断类型)

2字符串API加强(isBlank() 判断是不是空格;strip()去除首尾空格;stripTrailing()去除尾部空格;stripTrailing去除首部空格;stripLeading() 去除首部空格;repeat() 复制字符串;lines().count() 行数统计)

3集合API加强

添加了一个新的默认方法Collection.toArray(IntFunction)

// JDK11之前
Arrays.toString(names.toArray(new String[names.size()]));
// JDK11之后
Arrays.toString(names.toArray(size -> new String[size]));
image.gif

copyOf (),创建的集合为不可变集合,不能进行添加、删除、替换、排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

4Stream 加强(增加单个参数构造方法可为null,Stream.ofNullable(null); )

5Optional 加强(可以将一个 Optional 转换成一个 Stream)

Optional.of("javastack").orElseThrow();  // javastack
Optional.of("javastack").stream().count();  // 1
Optional.ofNullable(null).or(() -> Optional.of("javastack")).get();  // javastac
image.gif

6InputStream 加强(transferTo,可以用来将数据直接传输到 OutputStream)

7HTTP Client API(httpclient,在java11被标记为正式,改为java.net.http模块)

8一个命令编译运行源代码(不需要编译的javac命令)

9是8以后的稳定版

参考资料:JDK 11

相关文章
|
29天前
|
分布式计算 Java API
Java 8引入了流处理和函数式编程两大新特性
Java 8引入了流处理和函数式编程两大新特性。流处理提供了一种声明式的数据处理方式,使代码更简洁易读;函数式编程通过Lambda表达式和函数式接口,简化了代码书写,提高了灵活性。此外,Java 8还引入了Optional类、新的日期时间API等,进一步增强了编程能力。这些新特性使开发者能够编写更高效、更清晰的代码。
31 4
|
1月前
|
JSON 前端开发 JavaScript
java-ajax技术详解!!!
本文介绍了Ajax技术及其工作原理,包括其核心XMLHttpRequest对象的属性和方法。Ajax通过异步通信技术,实现在不重新加载整个页面的情况下更新部分网页内容。文章还详细描述了使用原生JavaScript实现Ajax的基本步骤,以及利用jQuery简化Ajax操作的方法。最后,介绍了JSON作为轻量级数据交换格式在Ajax应用中的使用,包括Java中JSON与对象的相互转换。
43 1
|
1月前
|
SQL Java 数据库连接
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,有效减少连接开销,提升访问效率。本文介绍了连接池的工作原理、优势及实现方法,并提供了HikariCP的示例代码。
48 3
|
1月前
|
SQL 监控 Java
Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面
本文探讨了Java连接池技术的最新发展,包括高性能与低延迟、智能化管理与监控、扩展性与兼容性等方面。同时,结合最佳实践,介绍了如何选择合适的连接池库、合理配置参数、使用监控工具及优化数据库操作,以实现高效稳定的数据库访问。示例代码展示了如何使用HikariCP连接池。
16 2
|
1月前
|
Java 数据库连接 数据库
深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能
在Java应用开发中,数据库操作常成为性能瓶颈。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接、减少连接建立和断开的开销,从而显著提升系统性能。文章介绍了连接池的优势、选择和使用方法,以及优化配置的技巧。
31 1
|
1月前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
46 1
|
1月前
|
SQL Java 数据库连接
打破瓶颈:利用Java连接池技术提升数据库访问效率
在Java应用中,数据库访问常成为性能瓶颈。连接池技术通过预建立并复用数据库连接,避免了频繁的连接建立和断开,显著提升了数据库访问效率。常见的连接池库包括HikariCP、C3P0和DBCP,它们提供了丰富的配置选项和强大的功能,帮助优化应用性能。
53 2
|
7月前
|
SQL Java 数据库连接
Java从入门到精通:3.1.2深入学习Java EE技术——Hibernate与MyBatis等ORM框架的掌握
Java从入门到精通:3.1.2深入学习Java EE技术——Hibernate与MyBatis等ORM框架的掌握
|
7月前
|
存储 设计模式 算法
Java从入门到精通:2.1.1深入学习Java核心技术——掌握Java集合框架
Java从入门到精通:2.1.1深入学习Java核心技术——掌握Java集合框架
|
7月前
|
算法 Java 程序员
论文翻译 | 【深入挖掘Java技术】「底层原理专题」深入分析一下并发编程之父Doug Lea的纽约州立大学的ForkJoin框架的本质和原理
本文深入探讨了一个Java框架的设计、实现及其性能。该框架遵循并行编程的理念,通过递归方式将问题分解为多个子任务,并利用工作窃取技术进行并行处理。所有子任务完成后,其结果被整合以形成完整的并行程序。 在总体设计上,该框架借鉴了Cilk工作窃取框架的核心理念。其核心技术主要聚焦于高效的任务队列构建和管理,以及工作线程的管理。经过实际性能测试,我们发现大多数程序的并行加速效果显著,但仍有优化空间,未来可能需要进一步研究改进方案。
87 3
论文翻译 | 【深入挖掘Java技术】「底层原理专题」深入分析一下并发编程之父Doug Lea的纽约州立大学的ForkJoin框架的本质和原理