散列表执行各类操做的时间都为O(1),常量时间,不管散列表多大,所需时间都相同。
平均状况下,查找与数组同样快,插入和删除速度与链表同样快。
填装因子: = 散列表占用位置 / 位置总数
填装因子越大,说明空闲位置越少,冲突越多,性能会降低。
一个小的经验规则:一旦填装因子大于0.7,就调整散列表的长度。
一个好的散列函数:SHA。java
数组支持按照下标随机访问数据的特性,散列表是数组的一种扩展,由数组演化而来。编程
开放寻址法
出现了冲突就从新探测一个空闲位置插入。
好比线性检测:
查找的过程当中,遍历到空闲位置结束。
删除的时候通常不是置空,而是标记为delete,由于置空会影响线性探测。
二次探测:二次探测和线性探测相比,探测的步长为2。
双重散列:不单单使用一个散列函数,若是第一个散列函数冲突,就使用第二个散列函数,依次类推。api
链表法
每一个元素位置再接一个链表。那个元素位置叫作桶或槽。多增长了链表的遍历。
时间复杂度和链表的长度成正比。散列比较均匀的话,k = n/m,n = 数据个数,m是槽的个数。数组
词典检查就是把全部单词存储在散列表中进行查找,验证是否能找到。空间不大,放在内存里就行。缓存
动态扩容,均摊扩容(插入到新的散列表中,同时把老的散列表中一个数据拿到新的里面。)服务器
当数据量比较小、装载因子小的时候,适合采用开放寻址法。这也是Java 中的 ThreadLocalMap 使用开放寻址法的缘由。数据结构
虽然链表法比较耗费空间,可是若是存储的是大对象,那么链表中的结点占用空间不多,因此能够忽略不计。
比较适合存储大对象,大数据量的散列表,能够用红黑树代替链表。编程语言
默认是初始大小是16,能够调节。
最大装载因子是0.75,启动扩容会扩大到两倍大小。函数
底层使用链表法解决冲突,JDK1.8版本中,链表长度超过8时,链表转化为红黑树。当红黑树结点少于8个的时候,红黑树转化为链表。性能
如下为其散列函数。
int hash(Object key) { int h = key.hashCode();//返回Java对象的hash code return (h ^ (h >>> 16)) & (capitity -1); //capicity 表示散列表的大小 }
将添加、删除、查找操做时间复杂度降到O(1)。
散列表中嵌入双向链表,双向链表增长特殊字段hnext。
结点除了可使用前驱后继指针访问以外,还能够经过hnext索引访问(拉链)。
疑惑点:为什么查找到一个结点以后将其放在链表尾部。
Redis有序集合
Redis和跳表关系很紧密
Java LinkedHashMap
LinkedHashMap能够支持按照插入顺序遍历数据,还支持按照访问顺序来遍历数据。
Linked指的是双向链表。
访问完数据以后也会放到链表结尾,估计是LRU策略(最近最少使用)
散列表没法按照某种顺序快速遍历,一个方法是拷贝到数组中,排序遍历。 散列表是动态数据结构,不停有插入、删除状况。若是按照顺序遍历散列表,那么经常会将散列表和链表(或者跳表)一块使用。