平时在工作中,会使用大量的 Hash,这种美妙的数据结构,在开发中给予了我们极大的帮助,非常值得深入学习。 我想通过本文,来梳理一下,到目前为止我在 HashMap 上粗浅的知识积淀。
文章的内容大致会有以下几个部分:
哈希的实现原理
哈希的冲突解决
哈希映射的应用
用哈希实现快速存取
一个爬虫系统在互联网上按照链接(url)爬取数据时,每爬取完一个链接,就将该链接保存在某个地方,标记为已爬去。当遇到下一个链接时,它需要快速的知道这个链接是否已经被爬取过,如果已经被解析过则转而爬下一个。
实现这个爬取标记的方法可以是:将爬取过的链接放到一个数组中,每准备爬一个链接,就到数组中来查找这个链接在不在数组中。不在数组中,说明没有爬取过。成功爬取后,就将这个链接追加到数组中,标记为已爬取。
此方案是可行的,不过,当爬取的数据量非常大时,上万甚至百万在互联网百亿级的链接数量中是非常渺小的。这个时候每次查询链接是否已经被爬取过,都需要遍历整个庞大的数组,时间复杂度为 O(N),性能可想而知会随着数据量的增大线性下降。
这时候,就需要一个能提供快速查找的数据结构,来替换数组方案。HashMap 就是这样的一种解决方案,它能在庞大的数据集中实现近似 O(1) 的存取时间复杂度。理想状态下,查询时间消耗不受数据量增加的影响,始终保持在一个常量范围。
本质上,用 HashMap 时,已经爬取过的链接数据也是存放到一个数组中的,只不过,通过 HashMap 存放的链接,可以快速地知道,自己是被放在数组的具体哪个下标(index),通过 index 就可以在数组中一下就将数据取到。A 是这个数组,A[index] 就直接取到数据,而不需要去遍历整个数组。
哈希如何实现
那么,HashMap 是怎么知道,某一个链接是具体存放到数组的哪一个下标中的呢? 这里面一定有一个映射关系在,通过这个映射关系,就知道数据是被存放到数组的哪个位置的。
到这里就必须得介绍一个神奇的东西,映射关系的秘密就藏在里面。
哈希函数
哈希函数也称做散列函数 , 是一种数据压缩算法,可以将任意长度的内容压缩到一个固定的范围,形成一个固定长度的摘要,实现内容体积的压缩。
给一个输入(链接或者其他随意内容),哈希函数会输出一个固定长度的摘要。相同的内容每次都会得到相同的摘要,不同的内容会得到不同且分散的摘要(理想情况下)。也就是说,通过哈希函数,可以得到某个文本和一段摘要之间稳定的映射关系。
哈希函数有很多种,不同的质量也有差异,具体的可以自行深入学习,这是一个知识深水区。
到这里,我们可以基本了解上面说的,链接和数组下标的稳定映射了。可以浅显理解为ÿ