Java:Map(二)——equals和hashCode
Map原理
HashMap根据Key得到Value的原理,是它在内部通过空间换时间,根据Key直接计算出Value所在的索引。
在Map内部,对Key作比较是通过equals()实现的,只要两个Key用equals()比较的结果相同,这两个Key就指向同一个Value。
所以,正确使用Map必须保证:作为Key的对象必须正确覆写equals()方法。也就是说,如果我们放入Key的如果是我们自己写的类,就要保证这个类正确覆写了equals()方法。
通过Key计算Value的索引是通过Key对象的hashCode()方法,它返回一个int,HashMap就是用这个方法定位到Value的索引,继而查找到Value。
因此,正确使用Map需要保证:
- Key类正确覆写equals()方法,两个相同的Key调用该方法返回true;
- Key类正确覆写hashCode()方法,且该方法也有两个规范:
- 两个相同Key的hashCode()必须相同;
- 两个不同Key的hashCode()尽量不同;
- 两个相同Key的hashCode()必须相同;
第一条规范equals()必须遵守,否则HashMap不能正常工作。
编写equals()
和List一节所说的equals()编写方法相同
2021.6.17:List——编写equals方法 - ShineLe - 博客园
编写hashCode()
在正确实现equals()的基础上实现hashCode(),对于相同的实例,应当返回相同的hashCode():
public class Person { String firstName; String lastName; int age; @Override int hashCode() { int h = 0; h = 31 * h + firstName.hashCode(); h = 31 * h + lastName.hashCode(); h = 31 * h + age; return h; } }
注意上文对三个字段firstName、lastName、age,都要参与hashCode的计算,并不是每个字段各返回一个h,而是它们共同参与一个h的构建。
由于String已经正确实现了hashCode(),所以我们在构建Person的hashCode()时,可以利用它,对它乘一个固定值,就能起到把Person的hashCode()均匀分布到整个int范围。
hashCode()与null值
不过,如果传入的字段中有null,上述代码工作起来会抛出NullPointerException,为了解决该问题,在计算hashCode时,常借用Objects.hash()计算:
int hashCode(){ return Objects.hash(firstName,lastName,age); }
equals()中用到的每个字段,都必须在hashCode()中参与计算;没用到的字段,不能在hashCode()中参与计算。
Map内部空间组织形式
初始大小与扩容
HashMap初始化时,默认数组大小只有16,对于任何Key,无论其hashCode()有多大,都可以简单通过:
int index = key.hashCode(() & 0xf; // 0xf = 15
把索引值确定在0~15。
当添加超过了一定数量的K-V后,HashMap会在内部自动扩容,每次扩容一倍。
扩容后,数组大小为32,此时对索引的计算,是通过:
int index = key.hashCode() & 0x1f; //0x1f=31
创建指定容量的HashMap
Map <String,Integer> map = new HashMap<>(10000);
虽然指定容量为10000,但是HashMap内部数组长总是2n,所以,实际数组被初始化为比10000大的16384(214)。
冲突避免
如果不同的Key指向同一个索引,那么在该索引处就不是单个的Value,而是包含了这两个Value的List——List< Entry<String,Person> >,实际查找时,查找到的是这个List,它还会遍历这个List,直到找到这个List中的正确的Key-Value。
尽量要减小碰撞——尽量使不同的Key的hashCode()不同。hashCode()方法编写的越好,HashMap效率越高。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性