Java之常用去重方法

Java之常用去重方法

  • 常规元素去重
    • 常用去重6大方法
      • 方式1:遍历原List赋值给新List(保持原序)
      • 方式2:Set集合去重(保持原序)
      • 方式3:Set集合去重——HashSet(原序打乱)
      • 方式4:Set集合去重——TreeSet(按字典顺序重排序)
      • 方式5:Set集合去重——LinkedHashSet(保持原序)
      • 方式6:Java8新特性Stream实现去重(保持原序)
  • 自定义对象去重
    • 常用去重3大方法
      • 方式1:Set集合去重
      • 方式2:Java8新特性Stream去重
      • 方式3:Java8新特性filter去重
      • 其他:Java8新特性补充
  • 源码分析
    • Effective Java
    • Java编程思想
  • 参考链接

常规元素去重

  • 实现原理

    针对List去重,除了遍历去重,建议利用Set集合不允许重复元素的特点,通过List和Set互转实现去重

常用去重6大方法

方式1:遍历原List赋值给新List(保持原序)

```java

    public static List ridRepeat1(List list){
        List newList = new ArrayList<>();
        list.forEach(str -> {
            if (!newList.contains(str)) {
                newList.add(str);
            }
        });
        return newList;
    }

```

方式2:Set集合去重(保持原序)

```java

    public static List ridRepeat2(List list){
        List newList = new ArrayList<>();
        Set newSet = new HashSet<>();
        list.forEach(str -> {
            if (newSet.add(str)) {
                newList.add(str);
            }
        });
        return newList;
    }

```

方式3:Set集合去重——HashSet(原序打乱)

```java

    public static List ridRepeat3(List list){
        //List newList = new ArrayList<>(new HashSet<>(list));
        Set newSet = new HashSet<>();
        List newList = new ArrayList<>();
        newSet.addAll(list);
        newList.addAll(newSet);
        return newList;
    }


```

方式4:Set集合去重——TreeSet(按字典顺序重排序)

```java

    public static List ridRepeat4(List list){
        List newList = new ArrayList<>(new TreeSet<>(list));
        return newList;
    }


```

方式5:Set集合去重——LinkedHashSet(保持原序)

```java

    public static List ridRepeat5(List list){
        List newList = new ArrayList(new LinkedHashSet(list));
        return newList;
    }


```

方式6:Java8新特性Stream实现去重(保持原序)

```java

    List strList = Arrays.asList("asdf", "sf", "pf", "c", "sg", "pg", "asdf", "pg");
    List 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 中的内容,总结如下:

    1. 在程序执行期间,只要equals方法的比较操作用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法必须始终如一地返回同一个整数。

    2. 如果两个对象根据equals方法比较是相等的,那么调用两个对象的hashCode方法必须返回相同的整数结果。

    3. 如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。

Java编程思想

  • 抽取内容

    设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode()都应该产生同样的值。如果在将一个对象用put()添加进HashMap时产生一个hashCdoe值,而用get()取出时却产生了另一个hashCode值,那么就无法获取该对象了。所以如果你的hashCode方法依赖于对象中易变的数据,用户就要当心了,因为此数据发生变化时,hashCode()方法就会生成一个不同的散列码。

参考链接

https://www.cnblogs.com/zjfjava/p/9897650.html

你可能感兴趣的:(Source,Code,java)