HashMap散列表
数据结构
HashMap是由动态链表和红黑树的结构组成,而组成这些结构的是一个个桶结构;当map中链表长度小于64并且深度小于6的时候map将保持动态链表的结构,当不满足就会转化为红黑树的结构。
散列表处理冲突的方法
-
开放地址法:如果根据key计算出的hash出现了冲突,则去寻找下一个空的桶位置去存放。
-
优缺点
- 优点:简单,表不满的时候性能好
- 缺点:邻插槽会形成“集群”,当这些簇填满阵列的时候性能会严重下降,因为是穷举搜索
-
线性探测法
-
二次探测法
- 可以将原本冲突时存放hash+1改为hash*hash,也就是平方探测法,这样可以防止聚集(可是这种方法对于后面插入的元素,探测时间会延长,也就是所谓“二次聚集”)
-
随机探测法
-
-
再散列函数法:二次hash,需要寻找合适的函数
-
链地址法:这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
- 优点:简单
- 缺点:当链表中存放大量元素的时候,查询效率下降,所以需要对数组进行扩容。
-
建立一个公共溢出区
线程安全的map
- Hashtable线程安全,但效率低,因为是Hashtable是使用synchronized的,所有线程竞争同一把锁;
- ConcurrentHashMap不仅线程安全而且效率高,因为它包含一个segment数组,将数据分段存储,给每一段数据配一把锁,也就是所谓的锁分段技术。
equals和hashCode
- equals是Object类的方法:主要是比较地址值
- 对于String字符串来说equals是比较字符串中的内容是否相同,而“==”是比较两个对象在内存中的地址值。另外StringBuffer中没有重新定义equals方法,所以它是来自Object类的。
- hashCode是用来计算对象在内存中的hashCode值
- 存对象到集合中是怎么判断一个对象是否已经在集合中呢?首先会用计算hashCode如果hashCode发生冲突了,则判断集中已经有这个对象(内存相同),如果hashCode没有相同,则再用equals判断是否是同一个对象,如果不是,才会添加到集合中。
- 也就是说两个对象,hashCode相同,那么他们指向的地址一定相同,并且equals也相同;如果hashCode不相同,也不能说明他们就是不同对象,要根据equals判断。
- 需要注意的是当
equals()
方法被override时,hashCode()
也要被override。按照一般hashCode()
方法的实现来说,相等的对象,它们的hash code一定相等。
HashMap
Hash是什么
- 通过对象的内部地址(也就是物理地址)转换成一个整数,然后该整数通过hash函数的算法就得到了hashcode,所以,hashcode是什么呢?就是在hash表中对应的位置。
Hash的计算
-
为什么右移 16 位,为什么要使用 ^ 位异或
-
HashMap 如何根据 hash 值找到数组中的对象
final Node<K,V> getNode(int hash, Object key) { Node<K, V>[] tab; Node<K, V> first, e; int n; K k; if((tab = table) != null && (n = tab.length) > 0 && // 需要关注下面这一行 (first = tab[(n - 1) & hash]) != null) { if( first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first;