Java8-Stream流操作List去重问题
作者:徐寿春 发布时间:2023-11-23 23:29:49
Java8Stream流操作List去重
根据属性去重整体去重使用
distinct
ArrayList<LabelInfoDTO> collect = labelInfoDTOS.stream().
collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(LabelInfoDTO::getLabelCode))), ArrayList::new));
List列表运用Java8的stream流按某字段去重
问题
项目中经常会遇到列表去重的问题,一般可使用Java8的stream()流提供的distinct()方法:list.stream().distinct()。
list的类型为List<String>、List<Integer>,list里的元素为简单包装类型。
或者List<Xxx>,其中Xxx为自定义对象类型,重写equals和hashCode方法,可根据业务情况来实现,如id相同即认为对象相等。
有时会遇到这种情况,需要对按对象里的某字段来去重。
例如:
@NoArgsConstructor
@AllArgsConstructor
@Data
class Book {
public static Book of(Long id, String name, String createTime) {
return new Book(id, name, Date.from(LocalDateTime.parse(createTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).atZone(ZoneId.systemDefault()).toInstant()));
}
private Long id;
private String name;
private Date createTime;
}
现在我们要按name字段来去重,假设list如下:
List<Book> books = new ArrayList<>();
books.add(Book.of(1L, "Thinking in Java", "2021-06-29 17:13:14"));
books.add(Book.of(2L, "Hibernate in action", "2021-06-29 18:13:14"));
books.add(Book.of(3L, "Thinking in Java", "2021-06-29 19:13:14"));
思路
1、重写Book类的equals和hashCode方法,以name来判断比较是否相同,然后用stream的distinct方法来去重
代码:
class Book {
...
@Override
public String toString() {
return String.format("(%s,%s,%s)", id, name, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(createTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equals(name, book.name);
}
}
List<Book> distinctNameBooks1 = books.stream().distinct().collect(Collectors.toList());
System.out.println(distinctNameBooks1);
总结:
通过重写equals和hashCode方法,按实际需求来比较,可直接使用stream的distinct方法去重,比较方便;
有时对象类不方便或者不能修改,如它已实现好或者是引用的三方包不能修改,该方法不能灵活地按字段来去重。
2、通过Collectors.collectingAndThen的Collectors.toCollection,里面用TreeSet在构造函数中指定字段
代码:
List<Book> distinctNameBooks2 = books.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getName()))), ArrayList::new));
System.out.println(distinctNameBooks2);
总结:
使用stream流提供的方法,代码很简洁,但不足是虽然实现了去重效果,但list里的顺序变化了,而有的场景需要保持顺序。
3、通过stream的filter方法来去重,定义一个去重方法,参数为Function类型,返回值为Predicate类型
代码:
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> map = new HashMap<>();
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
List<Book> distinctNameBooks3 = books.stream().filter(distinctByKey(o -> o.getName())).collect(Collectors.toList());
System.out.println(distinctNameBooks3);
总结:
通过封装定义一个去重方法,配合filter方法可灵活的按字段去重,保持了原列表的顺序,不足之处是内部定义了一个HashMap,有一定内存占用,并且多了一个方法定义。
4、通过stream的filter方法来去重,不定义去重方法,在外面创建HashMap
代码:
Map<Object, Boolean> map = new HashMap<>();
List<Book> distinctNameBooks4 = books.stream().filter(i -> map.putIfAbsent(i.getName(), Boolean.TRUE) == null).collect(Collectors.toList());
System.out.println(distinctNameBooks4);
总结:
仍然是配合filter方法实现去重,没有单独创建方法,临时定义一个HashMap,保持了原列表的顺序,不足之处是有一定内存占用。
PS:暂时没找到stream流原生支持的可按某字段去重并且保持原列表顺序的方法
来源:https://blog.csdn.net/weixin_44550490/article/details/123879410


猜你喜欢
- 一、概述定义一个值类型,其中包含固定值集合。枚举类型变量可以是此集合中的任意一个或多个值。枚举使用enum关键字来声明,与类同级。枚举本身可
- 题目:在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数例如在数组{7
- Android现在这么火,各种的设备也是琳琅满目,高中低等,大小屏幕都有,但是它始终未能达到iOS那样的令人称赞的卓越体验和性能,其操作的流
- StopWatch是位于org.springframework.util包下的一个工具类,通过它可方便的对程序部分代码进行计时(ms级别),
- package GraphicsCanvas;import java.awt.BorderLayout;import java.awt.Ca
- 由其他进制转换为十进制比较简单,下面着重谈一谈十进制如何化为其他进制。1.使用Java带有的方法Integer,最简单粗暴了,代码如下//使
- C#正则验证大全 Regex.IsMatch()正则表达式验证需要引入命名空间 using System.Text.RegularExpre
- 下载UEditorhttps://ueditor.baidu.com/website/download.html下载完整源码和JSP版本Sp
- 本篇文章,我们来讲解springcloud的服务注册和发现组件,上一章节我们讲解了如何搭建springcloud的多模块项目,已经新建了sp
- 文件上传在Web应用中非常普遍,要在Java Web环境中实现文件上传功能非常容易,因为网上已经有许多用Java开发的组件用于文件上传,本文
- 前言不知道你是否参加过拼多多上邀请微信好友砍价功能,这个功能实现首先需要考虑的就是获取微信用户的信息。获取用户信息就是获取公众号下微信用户的
- 今天在QQ空间看到一篇关于C#语言验证18位身份证号码的验证方法和实例代码,抽了些时间学习了一下,个人觉得还不错,所以把它记了下来,方便以后
- 库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库和动态库两种。 一、静态库和动态库的区别1. 静态函数库这类库的
- 前言古语有云:道为术之灵,术为道之体;以道统术,以术得道。其中:“道”指“规律、道理、理论”,“术”指“方法、技巧、技术”。意思是:“道”是
- 一、问题描述在C#中is,as,using关键字具有其特点及使用场景,其中is关键字用于检查该对象是否与给定类型兼容,as关键字用于将对象转
- intellj idea的强大之处就不多说了,相信每个用过它的人都会体会到,但是我们也会被他的复杂搞的晕头转向,尤其刚从eclipse转过来
- 定义队列是 Apache RocketMQ 中消息存储和传输的实际容器,也是 Apache RocketMQ 消息的最小存储单元。 Apac
- 1.介绍有时候我们在Linux中运行Java程序时,需要调用一些Shell命令和脚本。而Runtime.getRuntime().exec(
- 在Android中使用ImageView显示图片的时候发现图片显示不正,方向偏了或者倒过来了。 解决这个问题很自然想到的分两步走: 1、自动
- 我就废话不多说了,大家还是直接看代码吧~List<Order> list = new ArrayList<User>