equals、HashCode与实体类的设计

本文详细介绍了Java中equals和hashCode方法的使用原理及如何重写这些方法以实现对象的有效去重。针对自定义类,阐述了如何通过正确实现这两个方法确保在HashSet和HashMap中的唯一性,并解释了其对于提高集合操作效率的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

equals和HashCode都是用来去重的,即判断两个对象是否相等。如果是String类则我们直接用.equals()判断,如果是我们自己定义的类,需要有自己的判断方法,重写equals,如果是集合(HashSet、HashMap)判断加入的元素是否为重复,并且加入的元素是我们自己定义的类,这时用重写equals和HashCode两个一起判断:插入的元素是否相等(HashSet中),插入的key是否相同(HashMap中)。

设计一个标准的实体类四大原则:

1、封装:属性私有化,提供get/set方法等;

2、提供无参构造;

3、重写toString()、HashCode()、equals()这三个方法,(继承自根Object,不必须都重写按自己的需求定);

4、实现序列化接口(implements Serializable),这样类的对象可以经过二进制数据流进行传输。

例如:

class Person2 implements Serializable{
	private static final long serialVersionUID = 1L;
	private String name;
	private int age;
	public Person2(){}
	public Person2(String name, int age){
		this.name = name;
		this.age = age;
	}
// 重写equals,定义同一类型,并且属性name和age全部相等则实例对象相等 
	public boolean equals(Object obj){
		if(this == obj){
			return true;
		}
		if(!(obj instanceof Person2)){
			return false;
		}
		Person2 p = (Person2) obj;
		if(this.name.equals(p.name)&&this.age == p.age){
			return true;
		}else{
			return false;
		}
	}
//  重写HashCode将对象的某些信息映射成一个数值---散列值  
	public int hashCode(){
		return this.name.hashCode()*this.age;
	}
// 重写toString打印自己想要的格式,如果不重写默认直接继承Object类的toString 方法是获取对象在内存中的值可能会乱码 
	public String toString(){
		return "姓名:"+this.name+";年龄:"+this.age;
	}
}

1、首先说一下,什么情况下要重写toString,object类里的toString只是把字符串的直接打印,数字的要转化成字符再打印,而对象,则直接打印该对象的hash码。所以当你要想按照你想要的格式去字符串一些对象的时候,就需要重写toString了。比如一个Student对象,直接toString肯定是一个hash码。然而你想得到的比如是:name:***,age:***。这时就重写toString就是在toString里写:System.out.println(“name:”+student.getName);System.out.println(“age:”+student.getAge)。这样再toString就直接反回你想要的格式。通过查api我们就可以知道HashSet的toString是把s的值格式化成[*, * ,*],就是给s的加个中括号,而且用逗号分开。而HashMap的toString是把m的值格式化成{key1=value1,key2=value2,key3=value3}所以你打印出来的是那样的格式,这就是重写toString的作用

equals和HashCode都是用来鉴定两个对象是否相等的。

2、一般来讲,equals是给用户调用的,如果你想判断两个实例对象是否相等,可以在自己定义的类中重写equals方法,然后在代码中调用这个方法就可以判断是否相等了。

简单的说,equals就是从表面和自己定义的规范内容上看两个对象是否相等。

举个例子,有个学生类,属性只有姓名和性别,那么我们可以认为只要姓名和性别相等,那么就说这2个对象是相等的。

3、HashCode的方法只有在散列集合中用到,这样的散列集合包括HashSet、HashMap以及HashTable。

例如在集合存储中,判断并不再插入重复的元素,也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但是如果集合中已经存在一万条数据或者更多的数据,如果采用equals方法去逐一比较,效率必然是一个问题。此时hashCode方法的作用就体现出来了。

当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法(可是被重写的)与新元素进行比较,相同的话就不存了,不相同就散列其它的地址,所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值

将元素放在集合中的流程:


### Java 实体类中 `hashCode` 方法的实现注意事项 在 Java 中,`hashCode()` 是一个非常重要的方法,它通常用于集合框架中的散列表结构(如 `HashMap`, `HashSet`)。为了确保对象能够被正确存储和检索,开发者需要合理地重写该方法。 #### 1. 默认实现 默认情况下,`Object` 类提供了 `hashCode()` 的原生实现[^1]。此实现基于对象的内存地址计算哈希值。然而,在自定义实体类时,默认实现可能无法满足需求,因为两个逻辑相等的对象可能会有不同的内存地址,从而导致它们具有不同的哈希值。 #### 2. 自定义实现原则 当重写 `hashCode()` 方法时,应遵循以下基本原则: - **一致性**:只要对象的内容未改变,多次调用 `hashCode()` 应返回相同的结果。 - **分布均匀性**:不同对象产生的哈希值应该尽可能分布在较大的范围内,以减少冲突的可能性。 - **性能考虑**:计算哈希值的过程不应过于复杂,以免影响性能。 #### 3. 常见实现方式 以下是常见的 `hashCode()` 实现方式之一,适用于大多数场景: ```java @Override public int hashCode() { final int prime = 31; // 使用质数提高分布均匀性 int result = 1; result = prime * result + ((field1 == null) ? 0 : field1.hashCode()); result = prime * result + ((field2 == null) ? 0 : field2.hashCode()); return result; } ``` 在此代码片段中: - 质数 `prime` 提高了哈希值的分布均匀性。 - 如果字段为 `null`,则将其视为零贡献到最终结果中。 #### 4. 注意事项 - 当重写了 `equals()` 方法时,也必须同步重写 `hashCode()` 方法[^2]。这是因为两者之间存在约定关系——如果两个对象通过 `equals()` 判断为相等,则它们的 `hashCode()` 必须一致;反之不成立。 - 避免仅依赖单一属性来生成哈希值,除非该属性足以唯一标识对象。 - 尽量避免使用可变状态作为输入参数参哈希函数运算,这可能导致违反一致性原则。 #### 5. 示例分析 假设有一个简单的实体类 `Person`,其包含姓名 (`name`) 和年龄 (`age`) 属性。下面展示了如何为其提供合适的 `hashCode()` 及配套的 `equals()` 定义: ```java class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Person person = (Person) obj; return age == person.age && Objects.equals(name, person.name); } @Override public int hashCode() { return Objects.hash(name, age); } } ``` 上述例子利用了 `Objects.hash(...)` 工具简化了手动编写过程,并自动处理了潜在的空指针异常问题[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值