Java:Map(二)——equals和hashCode

Map原理

HashMap根据Key得到Value的原理,是它在内部通过空间换时间,根据Key直接计算出Value所在的索引。

Map内部,对Key作比较是通过equals()实现的,只要两个Keyequals()比较的结果相同,这两个Key就指向同一个Value

所以,正确使用Map必须保证:作为Key的对象必须正确覆写equals()方法。也就是说,如果我们放入Key的如果是我们自己写的类,就要保证这个类正确覆写equals()方法。

通过Key计算Value索引是通过Key对象的hashCode()方法,它返回一个intHashMap就是用这个方法定位到Value索引,继而查找到Value

因此,正确使用Map需要保证:

  1. Key类正确覆写equals()方法,两个相同的Key调用该方法返回true
  2. Key类正确覆写hashCode()方法,且该方法也有两个规范:
    • 两个相同KeyhashCode()必须相同;
    • 两个不同KeyhashCode()尽量不同;

第一条规范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(),所以我们在构建PersonhashCode()时,可以利用它,对它乘一个固定值,就能起到把PersonhashCode()均匀分布到整个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,而是包含了这两个ValueList——List< Entry<String,Person> >,实际查找时,查找到的是这个List,它还会遍历这个List,直到找到这个List中的正确的Key-Value

尽量要减小碰撞——尽量使不同的Key的hashCode()不同hashCode()方法编写的越好,HashMap效率越高。

 

posted @   ShineLe  阅读(859)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示