Java重写equals方法实现去重

本文详细解释了在Java中如何通过重写equals和hashCode方法来实现对象的去重,并提供了具体的代码示例。

我们经常会遇到一些业务,要对数据进行去重,我们可能首先想到的就是重写equals方法,但是结果却不尽人意,这是为啥呢?其实还需要重写hashCode方法,因为光重写equals方法是不够的


举个栗子

package com.eicky;

public class Test {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null){
            return false;
        }
        if (this == obj){
            return true;
        }
        if (obj instanceof Test){
            Test o = (Test) obj;
            return this.id == o.id;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return id;
    }

    /**
     * 这个是考虑到如果id是Integer类型的时候,没有具体值的时候就使用里面字符串的hashCode
     *我们知道Java所有的类都是继承Object的,所有的类也都继承了Object的方法和属性
     * eg: toString()、hashCode()、equals()等
     */
    //@Override
    //public int hashCode() {
    //    return name.hashCode();
    //}
}

啰嗦几句:

当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false

重写这两个方法的作用就是为了去重,方式就不用多说了吧(Set什么的都能玩).
我们默认的去比对是否相同是比对的地址,而重写这两个方法就相当于比较两个对象是否相同就是去比对对象里面的内容

<think>好的,用户现在的问题是关于Java中如何通过指定字段对集合进行。我需要先回顾一下用户提供的引用内容,看看有没有相关的信息可以利用。 首先,用户提供了三个引用。引用1提到了在Java中根据指定字段的基本概念,引用2详细讲解了使用Stream API按照字段方法,引用3则有一个使用distinct()方法的示例,但看起来是针对Map对象的,可能效果不佳,因为distinct()默认使用对象的equals方法,而Map的equals比较的是所有字段,这可能不符合用户的需求。用户真正需要的是根据特定字段,而不是整个对象的比较。 接下来,我需要考虑用户可能的实际应用场景。通常,在Java中处理对象集合时,尤其是根据某个字段,常用的方法包括使用Stream API配合自定义的过滤器,或者利用TreeSet和Comparator来保持唯一性。另外,可能还需要考虑使用第三方库如Guava的Equivalence,但用户可能更倾向于标准库的解决方案。 根据引用2的内容,使用Stream的collect和Collectors.toCollection方法结合TreeSet是一个有效的方法,可以按照指定字段。同时,使用filter和ConcurrentHashMap来维护已出现的字段值也是一种高效的方式,特别是处理大数据量时,因为ConcurrentHashMap的线程安全特性可能提升性能。 需要注意的是,用户的问题可能涉及到对象的不同字段,因此需要提供一个通用的方法,允许用户指定任意字段进行。例如,通过传递Function来提取字段值,然后在过滤器中检查该值是否已经存在于ConcurrentHashMap中。 此外,用户可能对性能有要求,尤其是在处理大量数据时。TreeSet的方法时间复杂度是O(n log n),而使用ConcurrentHashMap的方法则是O(n),但后者需要更多的内存来存储已见的字段值。需要根据具体情况选择合适的方法。 最后,用户可能需要具体的代码示例,所以应该提供两种方法实现:一种是基于TreeSet和自定义比较器,另一种是基于Stream的filter和ConcurrentHashMap。同时,解释每种方法的优缺点,帮助用户根据实际需求选择最合适的方案。</think>### Java集合按指定字段实现方法Java实现集合按指定字段,主要有以下几种方法: --- #### 方法一:使用Stream API + TreeSet(推荐) **原理**:通过`Collectors.toCollection()`配合`TreeSet`,利用自定义比较器实现 ```java // 示例对象类 class User { String id; String name; // getter/setter省略 } List<User> users = ...; // 原始集合 // 按name字段 List<User> result = users.stream() .collect(Collectors.collectingAndThen( Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new )); ``` **特点**: - 时间复杂度:$O(n \log n)$ - 支持自定义排序规则 - 适用于需要保持顺序的场景[^2] --- #### 方法二:使用Stream filter + 临时容器 **原理**:通过`ConcurrentHashMap`记录已出现的字段值 ```java List<User> result = users.stream() .filter( user -> ConcurrentHashMap.newKeySet() .add(user.getName()) // 以name字段为依据 ).collect(Collectors.toList()); ``` **特点**: - 时间复杂度:$O(n)$ - 线程安全 - 适用于大数据量场景[^2] --- #### 方法三:自定义比较器(通用对象) **原理**:重写`equals`和`hashCode`方法 ```java class User { String id; String name; @Override public boolean equals(Object o) { return ((User)o).name.equals(this.name); } @Override public int hashCode() { return name.hashCode(); } } // 使用方式 List<User> result = users.stream().distinct().collect(Collectors.toList()); ``` **注意**:此方法会永久修改对象的相等性判断逻辑[^3] --- #### 方法四:Guava工具库 **原理**:利用`Equivalence`定义字段比较规则 ```java List<User> result = users.stream() .filter(Lists.newArrayList( Equivalence.equals().onResultOf(User::getName))::add) .collect(Collectors.toList()); ``` **需要依赖**: ```xml <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency> ``` --- ### 方法选择建议 | 方法 | 适用场景 | 性能 | 修改原对象 | |------|----------|------|------------| | TreeSet | 需要排序的 | 中等 | 否 | | ConcurrentHashMap | 大数据量 | 高 | 否 | | 重写equals | 固定规则 | 高 | 是 | | Guava | 需要灵活规则 | 中等 | 否 | ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值