Java8-Stream流操作List去重问题

 更新时间:2022年11月11日 15:21:17   作者:徐寿春  
这篇文章主要介绍了Java8-Stream流操作List去重问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

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流原生支持的可按某字段去重并且保持原列表顺序的方法

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Python自定义计算时间过滤器实现过程解析

    Python自定义计算时间过滤器实现过程解析

    这篇文章主要介绍了Python自定义计算时间过滤器实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • SparkStreaming-Kafka通过指定偏移量获取数据实现

    SparkStreaming-Kafka通过指定偏移量获取数据实现

    这篇文章主要为大家介绍了SparkStreaming-Kafka通过指定偏移量获取数据,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-06-06
  • Java中Array、List、Map相互转换的方法详解

    Java中Array、List、Map相互转换的方法详解

    这篇文章主要介绍了Java中Array、List、Map相互转换的方法详解,在实际项目开发中或者一些算法面试题目中经常需要用到Java中这三种类型的相互转换,比如对于一个整型数组中寻找一个整数与所给的一个整数值相同,需要的朋友可以参考下
    2023-08-08
  • Java控制台实现猜拳游戏

    Java控制台实现猜拳游戏

    这篇文章主要为大家详细介绍了Java控制台实现猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-01-01
  • Java实现线程安全单例模式的五种方式的示例代码

    Java实现线程安全单例模式的五种方式的示例代码

    这篇文章主要介绍了Java中实现线程安全单例模式的五种方式:饿汉式、枚举单例、懒汉式、DCL懒汉式和静态内部类懒汉单例,感兴趣的可以了解一下
    2022-03-03
  • Java 互相关联的实体无限递归问题的解决

    Java 互相关联的实体无限递归问题的解决

    这篇文章主要介绍了Java 互相关联的实体无限递归问题的解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • springboot yml中profiles的巧妙用法(小白必看多环境配置)

    springboot yml中profiles的巧妙用法(小白必看多环境配置)

    这篇文章主要介绍了springboot yml中profiles的巧妙用法,非常适合多环境配置场景,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • JavaWeb之Filter过滤器详解

    JavaWeb之Filter过滤器详解

    本篇文章主要介绍了JavaWeb之Filter过滤器详解,实例分析了JavaWeb之Filter过滤器的使用技巧,非常具有实用价值,需要的朋友可以参考下。
    2017-03-03
  • Spring populateBean属性赋值和自动注入

    Spring populateBean属性赋值和自动注入

    这篇文章主要为大家介绍了Spring populateBean属性赋值和自动注入示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • SpringBoot中的自定义Banner详细解析

    SpringBoot中的自定义Banner详细解析

    这篇文章主要介绍了SpringBoot中的自定义Banner详细解析,Banner即横幅标语,我们在启动SpringBoot项目时会将Banner信息打印至控制台,我们可以输出一些图形、SpringBoot版本信息等内容,需要的朋友可以参考下
    2024-01-01

最新评论