在Java里集合中对象相等性如何判断_Javaequals与hashCode解析
发布时间 - 2026-01-23 00:00:00 点击率:次是的。equals()不重写时默认用==比较内存地址,导致逻辑相等的对象被视为不等;必须同时重写hashCode()以保证哈希集合正确查找,且二者参与比较的字段须严格一致。
equals() 方法不重写就等于 == 吗?
是的。Java 中所有类默认继承自 Object,其 equals() 方法底层就是用 == 比较两个引用是否指向同一内存地址。这意味着:即使两个 Person 对象字段值完全相同,只要不是同一个实例,equals() 就返回 false。
常见错误现象:
- 往
HashSet或HashMap里添加自定义对象后查不到 —— 因为没重写equals()和hashCode() -
list.contains(new Person("Alice", 25))总是返回false,哪怕列表里已有相同字段的对象
实操建议:
- 只要类会被用于集合查找、去重、作为
Map键,就必须重写equals() - 重写时只比较业务上“逻辑相等”的字段(比如
id或username),不要包含可变字段(如lastLoginTime) - 务必先用
==判断是否为同一引用,再判null,最后用Objects.equals()比较字段 —— 避免 NPE
为什么重写了 equals() 还必须重写 hashCode()?
因为哈希集合(HashSet、HashMap 的 key)依赖 hashCode() 快速定位桶位置。如果两个对象 equals() 返回 true,但 hashCode() 不同,它们会被散列到不同桶中,导致“明明存在却查不到”。
违反约定的后果:
-
HashSet.add(obj)可能重复添加逻辑相等的对象 -
map.get(key)返回null,即使该key已存在 - JVM 不报错,但行为不可预测 —— 这是最难调试的一类 bug
实操建议:
- 用
Objects.hash(field1, field2, ...)生成hashCode(),它自动处理null - 参与
hashCode()计算的字段,必须和equals()中比较的字段严格一致 - 避免在
hashCode()中使用可变字段(如普通 setter 修改的属性),否则对象加入集合后修改字段会导致哈希码变化,再也找不回来
IDE 自动生成的 equals/hashCode 安全吗?
IntelliJ / Eclipse 生成的代码基本可用,但有三个关键点容易被忽略:
- 默认会把所有非静态字段都纳入比较和哈希计算 —— 如果类里有
transient字段、缓存字段或懒加载代理(如 Hibernate 的LazyInitializationException相关字段),必须手动剔除 - 若字段是集合(如
List),生成的Objects.equals()调用是安全的;但若字段是自定义对象,且该对象没重写equals(),整个链路仍会失效 - 生成的
hashCode()使用Objects.hash()是对的,但要注意:如果字段本身重写了hashCode(),要确认其实现是否稳定(比如是否依赖运行时状态)
示例:一个典型的安全重写片段
public class User {
private final String username;
private final int age;
private transient String cacheToken; // 不参与 equals/hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() 
!= o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(username, user.username);
}
@Override
public int hashCode() {
return Objects.hash(username, age); // 不含 cacheToken
}
}
String、Integer 等包装类为什么可以直接用?
因为 JDK 已经为它们正确实现了 equals() 和 hashCode():比较的是值而非引用,且哈希值由值唯一确定。但要注意边界情况:
-
new String("abc").equals("abc")是true,但new String("abc") == "abc"是false -
Integer在 [-128, 127] 范围内会缓存,所以Integer.valueOf(100) == Integer.valueOf(100)成立;超出范围则不一定 - 永远别用
==比较两个Integer是否“值相等”,必须用.equals()
复杂点在于:一旦你组合这些类型(比如 Map),只要嵌套结构里的每一层都满足契约,整个集合的行为就是可预期的。最容易被忽略的是——你自己写的类,哪怕只作为某个多层嵌套对象中的一个字段,也必须守规矩。
# java
# 懒加载
# ai
# eclipse
# 为什么
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
悟空浏览器如何设置小说背景色_悟空浏览器背景色设置【方法】
如何快速生成ASP一键建站模板并优化安全性?
Chrome浏览器标签页分组怎么用_谷歌浏览器整理标签页技巧【效率】
Laravel Eloquent:优雅地将关联模型字段扁平化到主模型中
武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?
晋江文学城电脑版官网 晋江文学城网页版直接进入
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
Java类加载基本过程详细介绍
nginx修改上传文件大小限制的方法
Laravel中间件起什么作用_Laravel Middleware请求生命周期与自定义详解
googleplay官方入口在哪里_Google Play官方商店快速入口指南
Laravel中DTO是什么概念_在Laravel项目中使用数据传输对象(DTO)
如何在万网主机上快速搭建网站?
千库网官网入口推荐 千库网设计创意平台入口
Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】
Laravel如何实现本地化和多语言支持_Laravel多语言配置与翻译文件管理
Laravel怎么使用Blade模板引擎_Laravel模板继承与Component组件复用【手册】
HTML5空格和margin有啥区别_空格与外边距的使用场景【说明】
微信小程序 scroll-view组件实现列表页实例代码
Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧
如何注册花生壳免费域名并搭建个人网站?
焦点电影公司作品,电影焦点结局是什么?
Firefox Developer Edition开发者版本入口
DeepSeek是免费使用的吗 DeepSeek收费模式与Pro版本功能详解
微信小程序 闭包写法详细介绍
网易LOFTER官网链接 老福特网页版登录地址
佛山企业网站制作公司有哪些,沟通100网上服务官网?
Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能
如何用AWS免费套餐快速搭建高效网站?
智能起名网站制作软件有哪些,制作logo的软件?
如何快速查询网站的真实建站时间?
JavaScript如何操作视频_媒体API怎么控制播放
如何在橙子建站上传落地页?操作指南详解
Laravel怎么自定义错误页面_Laravel修改404和500页面模板
如何解决hover在ie6中的兼容性问题
Laravel怎么实现搜索功能_Laravel使用Eloquent实现模糊查询与多条件搜索【实例】
Laravel如何使用.env文件管理环境变量?(最佳实践)
JavaScript如何实现音频处理_Web Audio API如何工作?
Python文件流缓冲机制_IO性能解析【教程】
如何在万网自助建站中设置域名及备案?
Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程
西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?
电视网站制作tvbox接口,云海电视怎样自定义添加电视源?
如何快速搭建安全的FTP站点?
Laravel怎么实现搜索高亮功能_Laravel结合Scout与Algolia全文检索【实战】
如何选择PHP开源工具快速搭建网站?
如何快速搭建自助建站会员专属系统?
Linux安全能力提升路径_长期防护思维说明【指导】
网站制作报价单模板图片,小松挖机官方网站报价?
Mybatis 中的insertOrUpdate操作


