散列是什么?
散列是使用一个散列函数,将一个键映射到一个索引;
可以这么理解,ArrayList使用0-N为索引来保存N-1个数据,而散列实现的集合使用经过散列函数处理过的索引来保存数据,如此一来,ArrayList中的数据在内存中是连续的,而散列实现的集合就不一定了。
散列的缺点与缺点:
优点:插入、更新、删除、查找单个数据速度快,均是O(1)的算法复杂度;
缺点:不能有重复数据,且不能保证数据的有序性;
如何实现散列集合?
说到这里,我想散列的重点已经很明确了,那就是散列函数的设计,散列函数的目的在于将一个键值映射到一个索引,这个步骤分为两步,首先将键值转换成一个称为散列码的整型值,然后将散列码压缩为散列表中的一个索引;
散列表的概念:一般来说,散列表是一个存储数据的数组(也可以使用链表,不过使用数组居多),可以认为这个散列表就是我们所需要的散列集合(基于数组实现的散列集合);
对于查找一个数据而言,首先使用散列函数将数据转换为整型索引,再根据这个索引去散列表取出数据;
在实现散列集合之前还需要以下基本知识:
1.获取一个数据的散列码:
首先你得明确散列码是一个32位的整型数值,获取散列码的过程也就是将一个数据转换成int类型,Object类提供的hashCode方法返回的就是普通java对象的散列码,该散列码代表了这个对象的内存地址。
2.压缩散列码:
通过第一步获得的散列码可能是一个很大的数值,这样一来散列表就需要足够的长度来支持,实际上存储的数据数量远远小于散列表的长度,过分浪费了内存。
常用的压缩方法:h=hashCode&(N-1)
h是压缩后的散列码,hashCode是原散列码,N是设定的散列表长度,这样一来h的取值范围只会在0或者N-1;
3.处理散列码冲突:
压缩散列码会出现散列码冲突的问题,通常的处理方式有开放地址法和链地址法。
这二者的最大区别在于开放地址法会寻找新的地址存放数据,而链地址则会将这些冲突的数据存在同一个地址,本质上就是散