HashMap什么时候重写hashcode和equals方法,为什么需要重写

本文解析了HashSet如何通过HashMap实现去重,并探讨了Student类的hashCode和equals方法对HashSet的影响。同时,文章还深入介绍了HashMap的工作原理,包括元素的存储与检索过程。
HashSet内部是通过HashMap实现。只有使用排序的时候才使用TreeMap。否知使用HashMap。
HashSet  set = new HashSet
set.put(new Student(1,"aa") );
set.put(new Student(1,"aa") );
set.put(new Student(2,"aa") );
结果set内的元素为3个,没有去处重复的new Student(1,"aa") ?为什么呢?
这里由于两个new Student(1,"aa")是不一样的Student对象。而默认的Student类的hashcode是根据对象的引用算的。所以直接认为是两个不一样的对象,直接put进去了。所以需要重写hashcode方法,如果hashcode不一样则直接认为是不同对象,如下:
class Student {
        private int code;
        private Stirng name;
        public int hashcode() {
            return code*name.hashcode();
        }
    }

发现还是不对,还是put进去了呢?
这里重写的hashcode是一样的,所以还是put进去了。所以还需要重新equals方法。其实是有这样一个规定,如果hahscode一样时,则还需要继续调用equals方式看看对象是否相等。如下即可实现:
public boolean equals(Object o) {
     Student s = (Student ) o;
     if (name.equals(s.getName) && code == o.getCode()) {
          return true;
     }
     return false;
}

可以看到如果hashcode不一样就直接认为是不一样的对象,不需要再去equal比较,更加节省时间。
如果new Student(1,"aa")、new Student(1,"bb")。通过code和name算出的hashcode就可以算是不一样的对象,就不需要再去equals比较。
往往HashSet中存放的对象是否相等的逻辑都需要自己定义,而并不会直接用默认的引用来算,即一般都会重新hashcode和equals方法,而且同时需要重写。以后要注意哦。

HashMap的put和get也类似。
HashMap是底层实现时数组加链表。
        A.当put元素时:
              1.首先根据put元素的key获取hashcode,然后根据hashcode算出数组的下标位置,如果下标位置没有元素,直接放入元素即可。
              2.如果该下标位置有元素(即根据put元素的key算出的hashcode一样即重复了),则需要已有元素和put元素的key对象比较equals方法,如果equals不一样,则说明可以放入进map中。这里由于hashcode一样,所以得出的数组下标位置相同。所以会在该数组位置创建一个链表,后put进入的元素到放链表头,原来的元素向后移动。       
         B.当get元素时:
             根据元素的key获取hashcode,然后根据hashcode获取数组下标位置,如果只有一个元素则直接取出。如果该位置一个链表,则需要调用equals方法遍历链表中的所有元素与当前的元素比较,得到真正想要的对象。
可以看出如果根据hashcdoe算出的数组位置尽量的均匀分布,则可以避免遍历链表的情况,以提高性能。
所以 要求重写hashmap时,也要重写equals方法。以保证他们是相同的比较逻辑
<think>我们正在讨论Java中重写hashCode()equals()方法的原因。根据引用资料,这主要与使用哈希集合(如HashSet、HashMap)时保持对象一致性有关。让我整理关键点: 1. **目的**:确保在哈希表结构(如HashMap)中,逻辑相等的对象具有相同的哈希值,并能被正确识别为相等[^1][^3][^4]。 2. **不重写的后果**: - 若不重写hashCode(),即使两个对象内容相同(equals()返回true),它们的哈希值也不同(默认使用内存地址计算),导致在HashMap中被放入不同的桶(bucket),从而被当作不同对象存储[^2][^5]。 - 若只重写equals()而不重写hashCode(),两个"相等"对象可能有不同哈希值,在哈希集合中仍会被视为不同对象[^2]。 3. **重写规则**: - 若`a.equals(b)`,则`a.hashCode()`必须等于`b.hashCode()`(反之不一定成立)[^1][^4]。 - 重写后,哈希集合通过以下步骤判断对象是否重复: 1. 先比较哈希值(快速筛选) 2. 若哈希值相同,再用equals()比较内容[^4]。 **示例**:假设有一个`Student`类,包含`id``name`字段。若不重写这两个方法: ```java Student s1 = new Student(1, "Alice"); Student s2 = new Student(1, "Alice"); System.out.println(s1.equals(s2)); // 默认false(比较地址) System.out.println(s1.hashCode() == s2.hashCode()); // 默认false ``` 重写后: ```java @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return id == student.id && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(id, name); // 使用相同字段计算哈希值 } ``` 此时`s1.equals(s2)`为`true`,且哈希值相同,可正确存入HashSet/HashMap[^3]。 **总结**:若自定义类可能用于哈希集合的键(key),必须同时重写`hashCode()``equals()`,以维护哈希表的一致性正确性[^3]。 §§相关问题 1. 重写equals()方法时需要遵循哪些原则? 2. 如何正确实现hashCode()方法以避免哈希冲突? 3. 在哪些场景下不需要重写hashCode()equals()方法? </think> ### 重写 hashCode equals 方法的原因 在 Java 中,同时重写 `hashCode()` `equals()` 方法的主要原因与**哈希表数据结构**(如 `HashMap`, `HashSet` 等)的底层机制密切相关。以下是具体原因分析: #### 1. **确保哈希表正确性** - **哈希表工作原理**: 当对象存入 `HashMap` 或 `HashSet` 时,首先调用 `hashCode()` 计算桶位置(bucket index)。若桶中已存在对象,再调用 `equals()` 检查内容是否相等。 - **问题**: 若只重写 `equals()` 而不重写 `hashCode()`,两个内容相同的对象可能被分配到不同桶中(因默认 `hashCode()` 基于内存地址计算),导致哈希表存储重复对象[^2][^3]。 ```java // 示例:未重写 hashCode 时 Map<Student, String> map = new HashMap<>(); map.put(new Student("101", "Alice"), "A"); map.put(new Student("101", "Alice"), "A"); // 会存储两份!违反唯一性 ``` #### 2. **维护对象一致性规则** - **强制约定**: Java 规定:若 `a.equals(b)` 返回 `true`,则 `a.hashCode()` 必须等于 `b.hashCode()`[^1]。 - **违反后果**: 若不遵守此约定,依赖哈希的集合类(如 `HashSet`)会出现不可预测行为。例如: ```java Set<Student> set = new HashSet<>(); set.add(new Student("102", "Bob")); set.contains(new Student("102", "Bob")); // 可能返回 false ``` #### 3. **提升哈希表性能** - **两步验证机制**: 1. `hashCode()` 快速过滤不同桶的对象(高效) 2. `equals()` 精确比较同一桶内的对象(准确) - **优势**: 避免对所有元素逐个调用 `equals()`,大幅减少比较次数[^4]。 #### 4. **逻辑相等 vs 引用相等** - **默认行为问题**: `Object` 类的 `equals()` 默认比较内存地址,但实际业务中常需基于字段值判断逻辑相等(如 ID 相同的用户对象)。 - **解决方案**: 重写 `equals()` 定义字段比较逻辑,同时重写 `hashCode()` 确保逻辑相等的对象返回相同哈希值。 ### 重写示例 ```java class Student { private String id; private String name; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return Objects.equals(id, student.id) && Objects.equals(name, student.name); } @Override public int hashCode() { return Objects.hash(id, name); // 使用相同字段计算哈希值 } } ``` > **关键点**:`hashCode()` `equals()` 必须使用**相同的字段集合**进行计算[^4]。 ### 何时不需要重写? 若对象**仅用于非哈希集合**(如 `ArrayList`)且无需逻辑相等比较,可不重写。但一旦重写 `equals()`,**必须重写 `hashCode()`** [^3]。
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值