Java之常用去重方法

2023-10-29

常规元素去重

  • 实现原理

    针对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 中的内容,总结如下:

    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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java之常用去重方法 的相关文章

  • 为什么 Hashtable 不允许空键或空值?

    正如 JDK 文档中所指定的 Hashtable 不允许空键或空值 HashMap 允许一个空键和任意数量的空值 为什么是这样 Hashtable 是较旧的类 通常不鼓励使用它 也许他们看到了对 null 键的需要 更重要的是 null 值
  • Java - 将无符号十六进制字符串解析为有符号长整型

    我有一堆十六进制字符串 其中之一是 d1bc4f7154ac9edb 这是 3333702275990511909 的十六进制值 如果执行 Long toHexString d1bc4f7154ac9edb 这与您得到的十六进制相同 现在
  • 使用itext java库复制时pdf文件大小大大增加

    我正在尝试使用 Java 中的 itextpdf 库将现有的 pdf 文件复制到一些新文件中 我使用的是 itextpdf 5 5 10 版本 我在两种方式上都面临着不同的问题 PDFStamper 和 PdfCopy 当我使用 PDFSt
  • java 拖放

    我尝试熟悉java中的拖放 但我发现的所有教程都是 让我生气 我想要的只是从 JList 包含在名为 UserPanel 的自制 JPanel 中 拖动 PublicUserLabel 并将其放入从 JTabbedPanel 继承的自制类中
  • Android Studio 与 Google Play 服务的编译问题

    我正在运行 Android Studio 0 8 4 并在 Android Studio 0 8 2 上尝试过此操作 我正在运行 Java JDK 1 8 0 11 并尝试使用 JDK 1 8 0 05 每当我尝试构建我的 android
  • Android 信号 11 (SIGSEGV),代码 1 (SEGV_MAPERR) libwebviewchromium.so

    对于 android 4 4 我多次收到 Native crash at system lib libwebviewchromium so 错误 以下是设备包括 Xperia Z1 SO 01F 16 30 2 Galaxy Tab4 7
  • 使用 https 的 Web 服务身份验证给出错误

    我编写了一个简单的 Web 服务 并使用摘要和 HTTPS 身份验证来保护它 我已经使用 Java 中的 keytool 生成了我的证书 当我通过创建 war 文件在 Tomcat 中部署 Web 服务时 axis 的欢迎页面正确显示 但是
  • Google 表格使用 API 密钥而不是 client_secret.json

    In the QuickStart java示例Java 快速入门 https developers google com sheets api quickstart java他们使用OAuth client ID识别该应用程序 这会弹出一
  • 在 Eclipse 中删除空块之前的新行

    我更喜欢奥尔曼式 http en wikipedia org wiki Brace style Allman style大括号 例如 if foo magical prancing unicorn stuff 而不是 if foo unma
  • 扩展多个类

    我知道 Java 不支持多重继承 因为不允许扩展多个类 我只是想知道我的问题是否有解决方法 我有一个名为CustomAction需要扩展两个抽象类 BaseAction and QuoteBaseAction 我无法更改这些抽象类中的任何一
  • 从字符串中删除重音符号

    Android 中有没有什么方法 据我所知 没有 java text Normalizer 可以从字符串中删除任何重音 例如 变成 eau 如果可能的话 我想避免解析字符串来检查每个字符 java text NormalizerAndroi
  • 在约束验证器中使用 Guice 进行依赖注入

    我有一个在 ConstraintValidator 的实现中注入类的用例 我正在使用 Google guice 进行依赖项注入 目前无法在验证器内注入 我的场景的简化形式 内部模块 Provides Singleton public Ser
  • Hybris:如何在impex中导入zip文件中的媒体?

    我知道我们可以导入未像这样压缩的图像 siteResource jar com project initialdata constants ProjectInitialDataConstants projectinitialdata imp
  • 添加 char 和 int

    据我了解 字符是一个字符 即一个字母 一个digit 标点符号 制表符 空格或类似的东西 因此 当我这样做时 char c 1 System out println c 输出 1 正是我所期望的 那么为什么当我这样做时 int a 1 ch
  • 如何在一次操作中使用 Thymeleaf 检查 null 和空条件?

    有什么方法可以检查 Thymeleaf 中的 null 和empty 条件吗 方法一 1 variable1 variable2 variable3 2 variable null 3 variable 如果我们结合两个条件 例如 vari
  • log4j.properties 在 Wildfly 上无法正常工作

    我的类路径中有一个 log4j properties 文件 它位于 APP XX jar log4j properties 位置 我注意到在ear文件中我还可以在lib文件夹中找到log4j 1 2 17 jar 但无论我在 log4j p
  • 在 Tensorflow-lite Android 中将位图转换为 ByteBuffer(浮点)

    在用于图像分类的tensorflow lite android演示代码中 图像首先转换为ByteBuffer格式以获得更好的性能 这种从位图到浮点格式的转换以及随后到字节缓冲区的转换似乎是一个昂贵的操作 循环 按位运算符 float mem
  • 如何更改 JAX-WS Web 服务的地址位置

    我们目前已经公开了具有以下 URL 的 JAX RPC Web 服务 http xx xx xx xx myservice MYGatewaySoapHttpPort wsdl http xx xx xx xx myservice MYGa
  • 如何在不使用 -cp 开关的情况下在 Groovy 中自动加载数据库 jar?

    我想简化调用 Oracle 数据库的 Groovy 脚本的执行 如何将 ojdbc jar 添加到默认类路径以便我可以运行 groovy RunScript groovy 代替 groovy cp ojdbc5 jar RunScript
  • 用于生成 ISO 文件的 Maven 插件

    有没有可以生成ISO镜像的maven插件 我需要获取一些模块的输出 主要是包含 jar 的 zip 文件 并将它们组合成一个 ISO 映像 Thanks 现在有一个 ISO9660 maven 插件可以完成这项工作 https github

随机推荐