项目中经常会遇到列表去重的问题,一般可使用Java8的stream()流提供的distinct()方法:list.stream().distinct()
。
list的类型为List
、List
,list里的元素为简单包装类型。
或者List
,其中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 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 distinctNameBooks1 = books.stream().distinct().collect(Collectors.toList());
System.out.println(distinctNameBooks1);
总结:
通过重写equals
和hashCode
方法,按实际需求来比较,可直接使用stream的distinct方法去重,比较方便;
有时对象类不方便或者不能修改,如它已实现好或者是引用的三方包不能修改,该方法不能灵活地按字段来去重。
2、通过Collectors.collectingAndThen
的Collectors.toCollection
,里面用TreeSet
在构造函数中指定字段
代码:
List 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 Predicate distinctByKey(Function super T, Object> keyExtractor) {
Map
总结:
通过封装定义一个去重方法,配合filter
方法可灵活的按字段去重,保持了原列表的顺序,不足之处是内部定义了一个HashMap
,有一定内存占用,并且多了一个方法定义。
4、通过stream的filter
方法来去重,不定义去重方法,在外面创建HashMap
代码:
Map
总结:
仍然是配合filter
方法实现去重,没有单独创建方法,临时定义一个HashMap
,保持了原列表的顺序,不足之处是有一定内存占用。
PS:暂时没找到stream流原生支持的可按某字段去重并且保持原列表顺序的方法
java steam List指定字段去重 java steam List指定字段去重 - 路过sayhi - 博客园
Java1.8—使用Stream通过对象某个字段对集合进行去重 Java1.8—使用Stream通过对象某个字段对集合进行去重_自傷無色丶的博客-CSDN博客_java stream 字段去重