Java之常用去重方法
Java之常用去重方法常规元素去重常用去重6大方法方式1:遍历原List赋值给新List(保持原序)方式2:Set集合去重(保持原序)方式3:Set集合去重——HashSet(原序打乱)方式4:Set集合去重——TreeSet(按字典顺序重排序)方式5:Set集合去重——LinkedHashSet(保持原序)方式6:Java8新特性Stream实现去重(保持原序)自定义对象去重常用去重3大方法方式
Java之常用去重方法
常规元素去重
-
实现原理
针对List去重,除了遍历去重,建议利用Set集合不允许重复元素的特点,通过List和Set互转实现去重
常用去重6大方法
方式1:遍历原List赋值给新List(保持原序)
```java
public static List ridRepeat1(List<String> list){
List<String> newList = new ArrayList<>();
list.forEach(str -> {
if (!newList.contains(str)) {
newList.add(str);
}
});
return newList;
}
```
方式2:Set集合去重(保持原序)
```java
public static List ridRepeat2(List<String> list){
List<String> newList = new ArrayList<>();
Set<String> newSet = new HashSet<>();
list.forEach(str -> {
if (newSet.add(str)) {
newList.add(str);
}
});
return newList;
}
```
方式3:Set集合去重——HashSet(原序打乱)
```java
public static List ridRepeat3(List<String> list){
//List<String> newList = new ArrayList<>(new HashSet<>(list));
Set<String> newSet = new HashSet<>();
List<String> newList = new ArrayList<>();
newSet.addAll(list);
newList.addAll(newSet);
return newList;
}
```
方式4:Set集合去重——TreeSet(按字典顺序重排序)
```java
public static List ridRepeat4(List<String> list){
List<String> newList = new ArrayList<>(new TreeSet<>(list));
return newList;
}
```
方式5:Set集合去重——LinkedHashSet(保持原序)
```java
public static List ridRepeat5(List<String> list){
List<String> newList = new ArrayList<String>(new LinkedHashSet<String>(list));
return newList;
}
```
方式6:Java8新特性Stream实现去重(保持原序)
```java
List<String> strList = Arrays.asList("asdf", "sf", "pf", "c", "sg", "pg", "asdf", "pg");
List<String> newList = strList.stream().distinct().collect(Collectors.toList());
System.out.println(newList);
```
-
备注
distinct()方法默认是按照父类Object的equals与hashCode工作的。所以:
上面的方法在List元素为基本数据类型及String类型时是可以的,但是如果List集合元素为对象,不会奏效。
不过如果你的实体类对象使用了目前广泛使用的lombok插件相关注解如:@Data,那么就会自动帮你重写了equals与hashcode方法,当然如果你的需求是根据某几个核心字段属性判断去重,那么你就要在该类中自定义重写equals与hashcode方法了。
自定义对象去重
常用去重3大方法
方式1:Set集合去重
-
自定义实体类
package com.neighbor.nbsp.entity; import java.util.Objects; /** * @ClassName User * @Description TODO 用户实体类$ * @Author charlesYan * @Date 2020/2/18 11:49 * @Version 1.0 **/ public class User { private int id; private int age; private String name; public User() { } public User(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } @Override public String toString() { return "User{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { ////地址相等 if (this == o) return true; //非空性:对于任意非空引用x,x.equals(null)应该返回false if (o == null || getClass() != o.getClass()) return false; if(o instanceof User){ User user = (User) o; //需要比较的字段相等,则这两个对象相等 return age == user.age && Objects.equals(name, user.name); } return false; } @Override public int hashCode() { return Objects.hash(age, name); } }
-
测试main方法
public static void main(String[] args) { List<User> userList = new ArrayList<>(); userList.add(new User(1, 90, "ellin")); userList.add(new User(2, 98, "charles")); userList.add(new User(3, 78, "frank")); userList.add(new User(4, 78, "frank")); userList.add(new User(5, 90, "ellin")); //使用HashSet-无序 Set<User> userSet = new HashSet<>(); userSet.addAll(userList); System.out.println(userSet); //使用LinkedHashSet-有序 List<User> newUserList = new ArrayList<>(new LinkedHashSet<>(userList)); System.out.println(newUserList.toString()); }
方式2:Java8新特性Stream去重
-
特性介绍
该方式不能保持原列表顺序而是使用了TreeSet按照字典顺序排序后的列表,如果需求不需要按原顺序则可直接使用;
要想按原顺序,可以根据上面简写方式**再次加工处理使用stream流的sorted()**相关API写法 -
测试main方法
import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.toCollection; import static java.util.Comparator.comparing; public static void main(String[] args) { //根据name属性去重 List<User> lt = userList.stream().collect( collectingAndThen( toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new) ); System.out.println(lt); //根据name,age属性去重,无序 List<User> lt2 = userList.stream().collect( collectingAndThen( toCollection(() -> new TreeSet<>( comparing(o -> o.getName() + ";" + o.getAge()) )), ArrayList::new ) ); System.out.println(lt2); //根据name,age属性去重,保持原顺序 //该方法执行报错,欢迎大佬指点 List<User> lt3 = userList.stream().collect( collectingAndThen( toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getName() + "," + o.getAge()))), v -> v.stream().sorted().collect(Collectors.toList()) ) ); }
方式3:Java8新特性filter去重
-
实现方式
创建一个方法作为 Stream.filter() 的参数,其返回类型为 Predicate,原理就是判断一个元素能否加入到 Set 中去 -
案例代码
public static void main(String[] args) { List<User> userList = new ArrayList<>(); userList.add(new User(1, 90, "ellin")); userList.add(new User(2, 98, "charles")); userList.add(new User(3, 78, "frank")); userList.add(new User(4, 78, "frank")); userList.add(new User(5, 90, "jack")); userList.add(new User(6, 89, "jack")); ObjectMapper objectMapper = new ObjectMapper(); System.out.println("去重前:" + objectMapper.writeValueAsString(userList)); List<User> newUserList1 = userList.stream().distinct().collect(Collectors.toList()); System.out.println("distinct去重:" + objectMapper.writeValueAsString(newUserList1)); //将distinctByKey()方法作为filter()的参数,过滤不能加入到set中的元素 List<User> newUserList2 = userList.stream().filter( distinctByKey(o -> o.getName() + "," + o.getAge()) ).collect(Collectors.toList()); System.out.println("自定义方法去重:" + objectMapper.writeValueAsString(newUserList2)); } //自定义过滤方法 private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) { Set<Object> seen = ConcurrentHashMap.newKeySet(); return t -> seen.add(keyExtractor.apply(t)); }
其他:Java8新特性补充
-
案例代码
public static void main(String[] args) { List<User> userList = new ArrayList<>(); userList.add(new User(1, 90, "ellin")); userList.add(new User(2, 98, "charles")); userList.add(new User(3, 78, "frank")); userList.add(new User(4, 78, "frank")); userList.add(new User(5, 90, "jack")); userList.add(new User(6, 89, "jack")); userList.stream() .filter(p -> p.getAge() >= 80)//获取所有80岁以上的用户 // .sorted(Comparator.comparing(User::getAge))//依年纪升序排序 .sorted(Comparator.comparing(User::getAge).reversed())//依年纪降序排序 .collect(Collectors.toList()) .forEach(System.out::println); boolean isAllAdult = userList.stream(). allMatch(p -> p.getAge() > 18); System.out.println("是否所有的用户都大于18岁" + isAllAdult); }
源码分析
-
注意事项
**当equals方法被重写时,通常有必要重写 hashCode 方法,**以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码
Effective Java
-
抽取内容
根据《Effective Java》第二版的第九条:覆盖equals时总要覆盖hashCode 中的内容,总结如下:-
在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。
-
如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。
-
如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
-
Java编程思想
-
抽取内容
设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在将一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码。
参考链接
https://www.cnblogs.com/zjfjava/p/9897650.html
更多推荐
所有评论(0)