Java8特性大全(最新版)

article/2025/7/28 7:03:26

一、序言

Java8 是一个里程碑式的版本,凭借如下新特性,让人对其赞不绝口。

  • Lambda 表达式给代码构建带来了全新的风格和能力;
  • Steam API 丰富了集合操作,拓展了集合的能力;
  • 新日期时间 API 千呼万唤始出来;

随着对 Java8 新特性理解的深入,会被 Lambda 表达式(包含方法引用)、流式运算的美所迷恋,不由惊叹框架设计的美。

二、方法引用

Lambda 表达式是匿名函数,可以理解为一段可以用参数传递的代码(代码像数据一样传递)。Lambda 表达式的使用需要有函数式接口的支持。

方法引用是对特殊 Lambda 表达式的一种简化写法,当 Lambda 体中只调用一个方法,此方法满足函数式接口规范,此时可以使用::方法引用语法。

从语法表现力角度来讲,方法引用 > Lambda表达式 > 匿名内部类方法引用是高阶版的 Lambda 表达式,语言表达更为简洁,强烈推荐使用。

方法引用表达式无需显示声明被调用方法的参数,根据上下文自动注入。方法引用能够提高 Lambda 表达式语言的优雅性,代码更加简洁。下面以Comparator排序为例讲述如何借助方法引用构建优雅的代码。

(一)方法引用与排序

1、普通数据类型

普通数据类型相对较容易理解。

// 正向排序(方法引用)
Stream.of(11, 15, 11, 12).sorted(Integer::compareTo).forEach(System.out::println);
// 正向排序
Stream.of(11, 15, 11, 12).sorted(Comparator.naturalOrder()).forEach(System.out::println);
// 逆向排序
Stream.of(11, 15, 11, 12).sorted(Comparator.reverseOrder()).forEach(System.out::println);
2、对象数据类型

(1)数据完好

数据完好有两重含义,一是对象本身不为空;二是待比较对象的属性值不为空,以此为前提进行排序操作。

// 对集合按照年龄排序(正序排列)
Collections.sort(userList, Comparator.comparingInt(XUser::getAge));
// 对集合按照年龄排序(逆序排列)
Collections.sort(userList, Comparator.comparingInt(XUser::getAge).reversed());

此示例是以Integer类型展开的,同理Double类型、Long类型等数值类型处理方式相同。其中Comparator是排序过程中重要的类。

(2)数据缺失

数据缺失的含义是对象本身为空或者待比较对象属性为空,如果不进行处理,上述排序会出现空指针异常。

最常见的处理方式是通过流式运算中filter方法,过滤掉空指针数据,然后按照上述策略排序。

userList.stream().filter(e->e.getAge()!=null).collect(Collectors.toList());
3、字符串处理

少数开发者在构建实体类时,String类型遍地开花,在需要运算或者排序的场景下,String 的缺陷逐渐暴露出来。下面讲述字符串数值类型排序问题,即不修改数据类型的前提下完成期望的操作。

实体类

public class SUser {private Integer userId;private String UserName;// 本应该是Double类型,错误的使用为String类型private String score;
}

正序、逆序排序

// 对集合按照年龄排序(正序排列)
Collections.sort(userList, Comparator.comparingDouble(e -> new Double(e.getScore())));

数据类型转换排序时,使用 JDK 内置的 API 并不流畅,推荐使用commons-collection4包中的排序工具类。了解更多,请移步查看ComparatorUtils。

// 对集合按照年龄排序(逆序排列)
Collections.sort(userList, ComparatorUtils.reversedComparator(Comparator.comparingDouble(e -> new Double(e.getScore()))));

小结:通过以排序为例,实现 Comparator 接口、Lambda 表达式、方法引用三种方式相比较,代码可读性逐步提高。

(二)排序器

内置的排序器可以完成大多数场景的排序需求,当排序需求更加精细化时,适时引入第三方框架是比较好的选择。

1、单列排序

单列排序包含正序和逆序。

// 正序
Comparator<Person> comparator = Comparator.comparing(XUser::getUserName);
// 逆序
Comparator<Person> comparator = Comparator.comparing(XUser::getUserName).reversed();
2、多列排序

多列排序是指当待比较的元素有相等的值时,如何进行下一步排序。

// 默认多列均是正序排序
Comparator<XUser> comparator = Comparator.comparing(XUser::getUserName).thenComparing(XUser::getScore);
// 自定义正逆序
Comparator<XUser> comparator = Comparator.comparing(XUser::getUserName,Comparator.reverseOrder()).thenComparing(XUser::getScore,Comparator.reverseOrder());

三、Steam API

流的操作包含如下三个部分:创建流、中间流、关闭流,筛选去重映射排序属于流的中间操作,收集属于终止操作。Stream是流操作的基础关键类。

(一)创建流

(1)通过集合创建流

// 通过集合创建流
List<String> lists = new ArrayList<>();
lists.stream();

(2)通过数组创建流

// 通过数组创建流
String[] strings = new String[5];
Stream.of(strings);

应用较多的是通过集合创建流,然后经过中间操作,最后终止回集合。

(二)中间操作

1、筛选(filter)

筛选是指从(集合)流中筛选满足条件的子集,通过 Lambda 表达式生产型接口来实现。

// 通过断言型接口实现元素的过滤
stream.filter(x->x.getSalary()>10);

非空过滤

非空过滤包含两层内容:一是当前对象是否为空或者非空;二是当前对象的某属性是否为空或者非空。

筛选非空对象,语法stream.filter(Objects::nonNull)做非空断言。

// 非空断言
java.util.function.Predicate<Boolean> nonNull = Objects::nonNull;

查看Objects类了解更详细信息。

2、去重(distinct)

去重是指将(集合)流中重复的元素去除,通过 hashcode 和 equals 函数来判断是否是重复元素。去重操作实现了类似于 HashSet 的运算,对于对象元素流去重,需要重写 hashcode 和 equals 方法。

如果流中泛型对象使用 Lombok 插件,使用@Data注解默认重写了 hashcode 和 equals 方法,字段相同并且属性相同,则对象相等。更多内容可查看Lombok 使用手册

stream.distinct();
3、映射(map)

取出流中元素的某一列,然后配合收集以形成新的集合。

stream.map(x->x.getEmpId());

filtermap操作通常结合使用,取出流中某行某列的数据,建议先行后列的方式定位。

Optional<MainExportModel> model = data.stream().filter(e -> e.getResId().equals(resId)).findFirst();
if (model.isPresent()) {String itemName = model.get().getItemName();String itemType = model.get().getItemType();return new MainExportVo(itemId, itemName);
}
4、排序(sorted)

传统的Collectors类中的排序支持 List 实现类中的一部分排序,使用 stream 排序,能够覆盖所有的 List 实现类。

// 按照默认字典顺序排序
stream.sorted();
// 按照工资大小排序
stream.sorted((x,y)->Integer.compare(x.getSalary(),y.getSalary()));

(1)函数式接口排序

基于 Comparator 类中函数式方法,能够更加优雅的实现对象流的排序。

// 正向排序(默认)
pendingPeriod.stream().sorted(Comparator.comparingInt(ReservoirPeriodResult::getPeriod));
// 逆向排序
pendingPeriod.stream().sorted(Comparator.comparingInt(ReservoirPeriodResult::getPeriod).reversed());

(2)LocalDate 和 LocalDateTime 排序

新日期接口相比就接口,使用体验更加,因此越来越多的被应用,基于日期排序是常见的操作。

// 准备测试数据
Stream<DateModel> stream = Stream.of(new DateModel(LocalDate.of(2020, 1, 1)), new DateModel(LocalDate.of(2019, 1, 1)), new DateModel(LocalDate.of(2021, 1, 1)));

正序、逆序排序

// 正向排序(默认)
stream.sorted(Comparator.comparing(DateModel::getLocalDate)).forEach(System.out::println);
// 逆向排序
stream.sorted(Comparator.comparing(DateModel::getLocalDate).reversed()).forEach(System.out::println);
5、规约(reduce)

对流中的元素按照一定的策略计算。终止操作的底层逻辑都是由 reduce 实现的。

(三)终止操作

收集(collect)将流中的中间(计算)结果存储到集合中,方便后续进一步使用。为了方便对收集操作的理解,方便读者掌握收集操作,将收集分为普通收集高级收集

1、普通收集

(1)收集为List

默认返回的类型为ArrayList,可通过Collectors.toCollection(LinkedList::new)显示指明使用其它数据结构作为返回值容器。

List<String> collect = stream.collect(Collectors.toList());

由集合创建流的收集需注意:仅仅修改流字段中的内容,没有返回新类型,如下操作直接修改原始集合,无需处理返回值。

// 直接修改原始集合
userVos.stream().map(e -> e.setDeptName(hashMap.get(e.getDeptId()))).collect(Collectors.toList());

(2)收集为Set

默认返回类型为HashSet,可通过Collectors.toCollection(TreeSet::new)显示指明使用其它数据结构作为返回值容器。

Set<String> collect = stream.collect(Collectors.toSet());
2、高级收集

(1)收集为Map

默认返回类型为HashMap,可通过Collectors.toCollection(LinkedHashMap::new)显示指明使用其它数据结构作为返回值容器。

收集为Map的应用场景更为强大,下面对这个场景进行详细介绍。希望返回结果中能够建立IDNAME之间的匹配关系,最常见的场景是通过ID批量到数据库查询NAME,返回后再将原数据集中的ID替换成NAME

ID 到 NAME 映射

@Data
public class ItemEntity {private Integer itemId;private String itemName;
}

准备集合数据,此部分通常是从数据库查询的数据

// 模拟从数据库中查询批量的数据
List<ItemEntity> entityList = Stream.of(new ItemEntity(1,"A"), new ItemEntity(2,"B"), new ItemEntity(3,"C")).collect(Collectors.toList());

将集合数据转化成 ID 与 NAME 的 Map

// 将集合数据转化成ID与NAME的Map
Map<Integer, String> hashMap = entityList.stream().collect(Collectors.toMap(ItemEntity::getItemId, ItemEntity::getItemName));

IDObject类映射

@Data
public class ItemEntity {private Integer itemId;private String itemName;private Boolean status;
}

将集合数据转化成 ID 与实体类的 Map

// 将集合数据转化成ID与实体类的Map
Map<Integer, ItemEntity> hashMap = entityList.stream().collect(Collectors.toMap(ItemEntity::getItemId, e -> e));

其中Collectors类中的toMap参数是函数式接口参数,能够自定义返回值。

public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper) {return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}

(2)分组收集

流的分组收集操作在内存层次模拟了数据库层面的group by操作,下面演示流的分组操作。Collectors类提供了各种层次的分组操作支撑。

流的分组能力对应数据库中的聚合函数,目前大部分能在数据库中操作的聚合函数,都能在流中找到相应的能力。

// 默认使用List作为分组后承载容器
Map<Integer, List<XUser>> hashMap = xUsers.stream().collect(Collectors.groupingBy(XUser::getDeptId));// 显示指明使用List作为分组后承载容器
Map<Integer, List<XUser>> hashMap = xUsers.stream().collect(Collectors.groupingBy(XUser::getDeptId, Collectors.toList()));

映射后再分组

Map<Integer, List<String>> hashMap = xUsers.stream().collect(Collectors.groupingBy(XUser::getDeptId,Collectors.mapping(XUser::getUserName,Collectors.toList())));

四、Stream 拓展

(一)集合与对象互转

将对象包装成集合的形式和将集合拆解为对象的形式是常见的操作。

1、对象转集合

返回默认类型的集合实例

/*** 将单个对象转化为集合** @param t   对象实例* @param <T> 对象类型* @param <C> 集合类型* @return 包含对象的集合实例*/
public static <T, C extends Collection<T>> Collection<T> toCollection(T t) {return toCollection(t, ArrayList::new);
}

用户自定义返回的集合实例类型

/*** 将单个对象转化为集合** @param t        对象实例* @param supplier 集合工厂* @param <T>      对象类型* @param <C>      集合类型* @return 包含对象的集合实例*/
public static <T, C extends Collection<T>> Collection<T> toCollection(T t, Supplier<C> supplier) {return Stream.of(t).collect(Collectors.toCollection(supplier));
}
2、集合转对象

使用默认的排序规则,注意此处不是指自然顺序排序。

/*** 取出集合中第一个元素** @param collection 集合实例* @param <E>        集合中元素类型* @return 泛型类型*/
public static <E> E toObject(Collection<E> collection) {// 处理集合空指针异常Collection<E> coll = Optional.ofNullable(collection).orElseGet(ArrayList::new);// 此处可以对流进行排序,然后取出第一个元素return coll.stream().findFirst().orElse(null);
}

上述方法巧妙的解决两个方面的异常问题:一是集合实例引用空指针异常;二是集合下标越界异常。

(二)其它

1、并行计算

基于流式计算中的并行流,能够显著提高大数据下的计算效率,充分利用 CPU 核心数。

// 通过并行流实现数据累加
LongStream.rangeClosed(1,9999999999999999L).parallel().reduce(0,Long::sum);
2、序列数组

生成指定序列的数组或者集合。

// 方式一:生成数组
int[] ints = IntStream.rangeClosed(1, 100).toArray();
// 方式二:生成集合
List<Integer> list = Arrays.stream(ints).boxed().collect(Collectors.toList());

五、其它

(一)新日期时间 API

1、LocalDateTime
// 获取当前日期(包含时间)
LocalDateTime localDateTime = LocalDateTime.now();
// 获取当前日期
LocalDate localDate = localDateTime.toLocalDate();
// 获取当前时间
LocalTime localTime = localDateTime.toLocalTime();

日期格式化

// 月份MM需要大写、小时字母需要大写(小写表示12进制)
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
// 获取当前时间(字符串)
String dateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("dateTime = " + dateTime);
2、Duration
Duration duration = Duration.between(Instant.now(), Instant.now());
System.out.println("duration = " + duration);
3、获取当前时间戳

如下方式获取的是 13 位时间戳,单位是毫秒。

// 方式一
long now = Timestamp.valueOf(LocalDateTime.now()).getTime();
// 方式二
long now = Instant.now().toEpochMilli();

(二)Optional

在Optional类出现之前,null异常几乎折磨着每一位开发者,为了构建健壮的应用程序,不得不使用繁琐的if逻辑判断来回避空指针异常。解锁Optional类,让你编写的应用健壮性更上一层楼。

1、先判断后使用

ifPresent方法提供了先判断是否为空,后进一步使用的能力。

2、链式取值

链式取值是指,层层嵌套对象取值,在上层对象不为空的前提下,才能读取其属性值,然后继续调用,取出最终结果值。有时候只关心链末端的结果状态,即使中间状态为空,直接返回空值。如下提供了一种无 if 判断,代码简介紧凑的实现方式:

Optional<Long> optional = Optional.ofNullable(tokenService.getLoginUser(ServletUtils.getRequest())).map(LoginUser::getUser).map(SysUser::getUserId);
// 如果存在则返回,不存在返回空
Long userId = optional.orElse(null);

六、流的应用

(一)列表转树

传统方式下构建树形列表需要反复递归调用查询数据库,效率偏低。对于一棵结点较多的树,效率更低。这里提供一种只需调用一次数据库,通过流将列表转化为树的解决方式。

image-20211014133305884
/*** 列表转树** @param rootList     列表的全部数据集* @param parentId 第一级目录的父ID* @return 树形列表*/
public List<IndustryNode> getChildNode(List<Industry> rootList, String parentId) {List<IndustryNode> lists = rootList.stream().filter(e -> e.getParentId().equals(parentId)).map(IndustryNode::new).collect(toList());lists.forEach(e -> e.setChilds(getChildNode(rootList, e.getId())));return lists;
}

喜欢本文点个♥️赞♥️支持一下,如有需要,可通过微信dream4s与我联系。相关源码在GitHub,视频讲解在B站,本文收藏在博客天地。



http://chatgpt.dhexx.cn/article/jrKuNR5O.shtml

相关文章

Java 8特性之Optional详解

一、Optional类 简介 Optional类是 Java 8 引入的一个很有趣的特性。它主要解决的问题是臭名昭著的空指针异常&#xff08;NullPointerException&#xff09; Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true&#xff0c;调用get()方法会返回…

Java基础之java8 新特性

Java基础之java8 新特性 一、Lambda 表达式二、Stream 初体验三、方法引用四、默认方法五、Optional 类六、Base 64七、字符串拼接八、 equals 与 instanceof九、final 一、Lambda 表达式 Lambda 表达式是在 Java8 中引入的&#xff0c;并号称是 Java8 的最大的特点。Lambda 表…

java8新特性Lambda和Stream以及函数式接口等新特性介绍

主要内容 1.Lambda 表达式 2.函数式接口 3.方法引用与构造器引用 4.Stream API 5.接口中的默认方法与静态方法 6.新时间日期API 7.其他新特性 Java 8新特性简介 速度更快速度更快代码更少&#xff08;增加了新的语法Lambda 表达式&#xff09;强大的Stream API便于并行最大化…

python 读取并显示图片的两种方法

转自&#xff1a;http://www.cnblogs.com/yinxiangnan-charles/p/5928689.html 在 python 中除了用 opencv&#xff0c;也可以用 matplotlib 和 PIL 这两个库操作图片。本人偏爱 matpoltlib&#xff0c;因为它的语法更像 matlab。 一、matplotlib 1. 显示图片 import matplotli…

Python同时显示多张图片在一个画面中(两种方法)

很多时候需要把很多图片同时显示到一个画面中&#xff0c;现在分享两个方法&#xff0c;这里我恰好拿之前写的爬取网上图片保存到本地的爬虫模型爬一些图片作为素材Python 爬虫批量爬取网页图片保存到本地。 得到素材如下所示&#xff1a; 现在让这些图片同时显示。 方法一 …

python 打开 显示图片

import matplotlib.pyplot as plt # plt 用于显示图片 from PIL import Imageplt.figure() plt.plot([1,2], [1,2])plt.rcParams[font.sans-serif] [SimHei] # 中文乱码 plt.imshow(Image.open("b.png"))plt.axis(off)plt.tight_layout() manager plt.get_current…

python 把matplotlib绘制的图片显示到html中

需求 一般网页中图片显示是给url链接过去&#xff0c;但是有的时候显示的图表是临时计算绘制的&#xff0c;并不需要保存&#xff0c;因此就需要直接显示一个图片的方法。 灵感是来自于jupyter&#xff0c;发现他是这样的&#xff1a; 估计是base64编码了。 查了一下如何把ma…

python如何将图片显示在网页上

from flask import Flask, render_template_string import base64 import cv2import osapp Flask(__name__)# 读取图像app.route(/)def index():# 读取图像文件并将其转换为Base64编码的字符串img_path 1.pngimg_data open(img_path, rb).read()img_base64 base64.b64encod…

python matplotlib 显示图像

python matplotlib 显示图像 首先需要import import os from PIL import Image import matplotlib.pyplot as plt 显示一幅彩色图片 img Image.open(os.path.join(images, 2007_000648 .jpg))plt.figure("Image") # 图像窗口名称 plt.imshow(img) plt.axis(on)…

python中plt.imshow()不显示图片

python画图函数可能是需要一些设置&#xff0c;或者一些特定的函数运行才会出现图片的。 Python中plt.imshow(image)无法显示图片解决办法 使用plt.imshow()发现不能显示图片&#xff0c;加了plt.show()也还是不能显示 先引入包pylab import pylab 然后在plt.imshow(img)后面…

Python tkinter之PhotoImage图片显示问题

作为Python小白的你&#xff0c;有没有遇到过这几个问题&#xff1f; PhotoImage不支持jpg等图片格式&#xff1b;将PhotoImage放在函数里图片不显示&#xff1b;循环加入图片只显示一张&#xff1b; 这些难倒了一大批小白&#xff0c;那么接下来&#xff0c;本文教你如何解决…

python图显示在新的窗口_python在新的图片窗口显示图片(图像)的方法

python在新的图片窗口显示图片(图像)的方法 使用python画图,发现生成的图片在console里。不仅感觉很别扭,很多功能也没法实现(比如希望在一幅图里画两条曲线)。 想像matlab一样单独地生成一个图片窗口,然后我在网上找了一个多小时,都没有找到想要的,要么仅仅是画图的教…

python人工智能-动态显示图片的几种办法实现

python人工智能动态显示图片 任务简介&#xff0c;在电脑创建一个文件加&#xff0c;利用代码往文件加入照片文件&#xff0c; 然后动态显示该文件夹中的照片&#xff0c;显示给用户观看&#xff0c;可以利用open-cv&#xff0c;matplotlib,PIL等技术实现 这里是电脑一个装有照…

Python 3 显示图像的方法

如果你正在寻找一个库来帮助你在 Python 中显示图像&#xff0c;那么你很幸运。Python 有许多不同的库可用于显示图像。三个最受欢迎的库是Pickel、Matplotlib 和 OpenCV。 到底应该选哪一个&#xff0c;还是看自己的个人喜好了。我喜欢并使用 OpenCV 而不是 Pillow 和 Matplot…

python显示图片_python图像显示

广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 使用python进行数字图片处理,还得安装pillow包。 虽然python里面自带一个pil(python images library), 但这个库现在已经停止更新了,所以使用pillow, 它是由p…

python显示图片

python显示图片两种方法 1.利用Pillow包 未安装的可以通过pip install Pillow安装 from PIL import Image img Image.open(notMNIST_large/A/aGFua28udHRm.png) img.show() 显示的图片如下&#xff1a; 2.利用matplotlib 同样可以通过pip进行安装 from PIL import Image …

使用Python查看并显示图像

今天又来写一个Python的基础用法&#xff0c;使用Python来显示图像&#xff0c;不多说&#xff0c;代码如下&#xff1a; import matplotlib.pyplot as plt from matplotlib.image import imreadimg imread(C:/Users/xx/Desktop/xx.jpg) plt.imshow(img)plt.show() 2022.05.1…

基于python多光谱遥感数据处理、图像分类、定量评估及机器学习方法应用

普通数码相机记录了红、绿、蓝三种波长的光&#xff0c;多光谱成像技术除了记录这三种波长光之外&#xff0c;还可以记录其他波长&#xff08;例如&#xff1a;近红外、热红外等&#xff09;光的信息。与昂贵、不易获取的高光谱、高空间分辨率卫星数据相比&#xff0c;中等分辨…

推扫式和快照式高光谱成像系统在红肉掺假检测中的应用

目录 摘要 推扫式高光谱成像系统的应用 快照式高光谱成像系统的应用 总结 参考文献 摘要 肉类掺假常见于用低价肉代替高价肉获取非法利益&#xff0c;它不但影响了食品的质量&#xff0c;甚至会威胁到人们的健康。目前的检测手段一般是取样后送样检测&#xff0c;很难实时…

不同光谱信息的图像比较

首先&#xff0c;我们要知道普通光学相机在自然光下拍的照片其实并不包含完整的光谱信息&#xff0c;一般在遥感领域&#xff0c;多/高光谱数据应该包含近红外波段&#xff0c;例如Landsat有如下波段&#xff1a; 编号 波长&#xff08;m&#xff09; 1 0.433–0.453 深蓝/紫…