引言
以前面试的时候被面试官问到过这样一个问题:
你有没有重写过 hashCode 方法?
心里想着我没事重写哪玩意干啥,能不写就不写。嘴上当然没敢这么说,只能略表遗憾的说抱歉,我没写过。
撇了面试官一眼,明显看到他对这个回答不满意,但是这已经触及到我的知识盲点了,我也很惭愧,可是确实没有重写过,咱也不能胡扯不是。
然后他又问到另外一个问题:
你在用 HashMap 的时候,键(Key)部分,有没有放过自定义对象?
我说我放过,很自信的说我放过(其实我忘了我有没有放过),但是不能怂啊,第一个都不会了,第二个再说不会哪不是直接拜拜要走人了吗?
面试官狡猾的笑了,说是你既然没有重写过 hashCode 方法,你怎么把自定义对象放进去的?
我勒个去,原来你在这等着我呢,没想到这还是个连环炮,惹不起惹不起,认怂三连
![97ff0e42d3de8e26b5386bee55253629.png](https://i-blog.csdnimg.cn/blog_migrate/50340d95e888689c72d97fb00d381b68.jpeg)
不过不会就学,不懂就问,这一直都是咱程序猿优秀的素养,今天就干脆从 Hash 表学起,讲述 HashMap 的存取数据规则,由此来搞定上述问题的答案。
通过 Hash 算法来了解 HashMap 对象的高效性
我们先复习数据结构里的一个知识点:
在一个长度为 n(假设是100)的线性表(假设是 ArrayList)里,存放着无序的数字;如果我们要找一个指定的数字,就不得不通过从头到尾依次遍历来查找,这样的平均查找次数是 n / 2(这里是50)。
我们再来观察 Hash 表(这里所说的 Hash 表纯粹是数据结构上的概念,和 Java 无关)。
哈希表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即 key,即可查找到其对应的值。
它的平均查找次数接近于 1,代价相当小。
使用哈希查找有两个步骤:
- 使用哈希函数将被查找的键转换为数组的索引:在理想的情况下,不同的键会被转换为不同的索引值,但是在有些情况下我们需要处理多个键被哈希到同一个索引值的情况。所以哈希查找的第二个步骤就是处理冲突
- 处理哈希碰撞冲突:有很多处理哈希碰撞冲突的方法,本文后面会介绍拉链法和线性探测法。
既然哈希查找第一步就是使用哈希函数将键映射成索引,那我们就先假设一个 Hash 函数是x * x % 5,(当然实际编程中不可能用这么简单的 Hash 函数,一般选择的哈希函数都是要易于计算并且能够均匀分布所有键的,这里纯粹为了说明方便),然后假设 Hash 表是一个长度是 11 的线性表。
接下来如果我们如果要把 6 放入其中,那么我们首先会对 6 用 Hash 函数计算一下,结果是 1,所以我们就把 6 放入到索引号是 1 这个位置。同样如果我们要放数字 7,经过 H