hash、hashCode、hashMap和 hashTable浅析

Hash,一般直接音译成“哈希”,按真正含义译作“散列”比较合适。通过散列算法,把任意长度的输入,转换成固定长度的输出,输出就叫做散列值(hashCode)。这种转换是一种压缩映射,也就是说,散列值所占用的空间通常远小于输入值所占用的空间,不同的输入可能会有相同的散列值。散列的目的,在于尽量分散数据的存储位置,使数据散列在不同的哈希桶(bucket)中。

hashCode:是一串固定长度的整型的数字,hashCode可以由hash函数生成。hash函数常用的算法有:直接取余法、乘法取整法、平方取中法等。由hashCode可以得到对象在hash表中的位置(具体如何获取位置,后面会讲到)。

hashMap、hashTable:都是基于hash表实现的,都通过单链表解决数据通途的问题。二者很类似,但是也有很明显的区别。下面详细介绍一下其内部原理。

HashMap的内部原理:

在JDK1.8之前,HashMap的内部是通过 数组+链表 实现的,在JDK1.8及1.8版本以后,HashMap的内部是通过 数组+链表+红黑树 实现的,新增了红黑树的部分(链表在长度大于8时,转为红黑树)。笔者目前只了解了 数组+链表的实现方式,红黑树的部分有待于日后继续学习。 此处讲解的是 数组+链表 的实现方式。


上图是HashMap的内部结构。左侧是一个数组,右侧是一个个的链表,数组中存放的是链表的头结点。采用链表是为了解决hash冲突的问题。

那么向hashMap的写入一个Key-Value对是怎样一个过程呢?


1、通过Hash函数,将Key转换成长度固定的hashCode;

2、hashCode对数组容量取余(初始容量16)得到一个位置索引;

扫描二维码关注公众号,回复: 8639374 查看本文章

3、通过位置索引,可以得到数组在该位置保存的链表的头结点;

4、如果头结点中没有数据,则将Value直接写入头结点,如果头结点中有数据,则新建一个头结点,将Value写入新建的头结点,新建的头结点指向原来的头结点(why?查资料说是为了防止尾部遍历。。不懂)。

这样就完成了Key-Value的插入。

读取过程与写入过程类似,但是有一些新内容,下面说一下根据Key读取Value的过程:

前3个步骤与写入过程相同,此处不再赘述,从第4步说起

4、得到链表的头结点,就相当于获得了整个链表。但是这时候我们会发现,这些链表的长度有可能是不一样的。有的链表长度为1,有的链表长度大于1(hashCode相同)。如果长度为1,可以很轻松的拿到对应的Value值,但是如果链表的长度大于1,我们应该取哪一个值当做Key值对应的Value呢?这就是hashCode的碰撞问题。

这时候,会使用到equals()方法。具体流程是: 由hashCode定位到 数据所在的链表,链表中存在多个Value时,再通过 Key的equals()方法,与链表中的各个key比较,key相同的那个节点保存的Value就是要读取的那个Value。


补充:

HashMap中的数组容量默认是16(初始容量),当hashMap中的数据量一步步增大,超过阀值(加载因子,默认值是0.75)时,会自动扩展。扩展的过程是:创建一个新的HashMap,将原hashMap中的数据添加到新的hashMap中,这中间涉及到重新计算元素在数组中的位置索引,所以会非常耗时。所以在适应过程中,最好提前预估一下数据量,在hashMap初始化的时候,设置合适的 初始容量 和 加载因子。


hashMap 与 hashTable 的区别:

1、hashMap 是非synchronized的, 相应的也就是非线程安全的。而hashTable是synchronized的,是线程安全的。 如果存在多个线程同时读写同一个hashMap,那么需要手动加锁才能保证线程安全,而hashTable的方法本身就有锁,不需要手动加锁。

也正是因为hashTable的方法本身有锁,所以hashTable的读取速度比hashMap要慢。如果是单线程,推荐使用hashMap 。如果是多线程,为了保证线程安全,推荐使用hashTable(在java5以后,hashTable逐渐被舍弃,可以使用ConcurrentHashMap完成与hashTable相同的功能)。

2、hashMap中 , Key 和 Value 都可以是null , 在hashTable中,Key和value都不能是null

3、hashMap 的默认容量是16 , hashTable的默认容量是11 。 hashMap要求底层的数组容量必须是2的整数次幂, hashTable不要求。在数据量达到超过阀值时,二者都会自动扩展,hashMap会自动扩展到原容量的2倍, hashTable会自动扩展到原容量的2倍+1。

4、 是否包含contains方法。 hashMap把contains方法去掉了, 改成了 containsKey() 和 containsValue()方法。  hashTable有 contains() 、containsKey()、containsValue()方法 , 其中 contains()和containsValue()功能相同。

5、HashMap继承自AbstractMap类,Hashtable继承自Dictionary类。但二者都实现了Map接口。

总结:hashMap 和 hashTable 很类似 , 具体选用那种结构,可以根据使用场景中对 线程安全性、同步、以及读写速度 三个要素进行选型。 而且,hashTable会逐渐废弃 , 取而代之的是 concurrentHashMap 。


扩展连接:https://www.cnblogs.com/chengxiao/p/6059914.html


发布了27 篇原创文章 · 获赞 43 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/u010476994/article/details/80049715
今日推荐